From a24bc2e76a499485f0634f5a8cdff282f4fc0f6a Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 7 Dec 2023 04:40:07 +0800 Subject: [PATCH 01/73] Implement semantic tokens lsp plugin draft --- cabal.project | 9 +- flake.nix | 2 +- haskell-language-server.cabal | 12 + hls-plugin-api/src/Ide/Types.hs | 9 + plugins/hls-semantic-tokens-plugin/LICENSE | 201 +++++++++++ .../hls-semantic-tokens-plugin.cabal | 77 +++++ .../src/Ide/Plugin/SemanticTokens.hs | 14 + .../src/Ide/Plugin/SemanticTokens/Internal.hs | 315 ++++++++++++++++++ .../src/Ide/Plugin/SemanticTokens/Types.hs | 112 +++++++ .../src/Ide/Plugin/SemanticTokens/query.hs | 15 + .../hls-semantic-tokens-plugin/test/Main.hs | 171 ++++++++++ .../test/testdata/T1.hs | 41 +++ src/HlsPlugins.hs | 8 + stack.yaml | 3 +- 14 files changed, 983 insertions(+), 6 deletions(-) create mode 100644 plugins/hls-semantic-tokens-plugin/LICENSE create mode 100644 plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal create mode 100644 plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs create mode 100644 plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs create mode 100644 plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs create mode 100644 plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/Main.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/T1.hs diff --git a/cabal.project b/cabal.project index d68a81b15e..a12e78a84a 100644 --- a/cabal.project +++ b/cabal.project @@ -34,6 +34,7 @@ packages: ./plugins/hls-explicit-record-fields-plugin ./plugins/hls-refactor-plugin ./plugins/hls-overloaded-record-dot-plugin + ./plugins/hls-semantic-tokens-plugin index-state: 2023-12-13T00:00:00Z @@ -55,8 +56,8 @@ constraints: text -simdutf, ghc-check -ghc-check-use-package-abis, ghc-lib-parser-ex -auto, - -- This is only present in some versions, and it's on by default since - -- 0.14.5.0, but there are some versions we allow that need this + -- This is only present in some versions, and it's on by default since + -- 0.14.5.0, but there are some versions we allow that need this -- setting stylish-haskell +ghc-lib, -- Centos 7 comes with an old gcc version that doesn't know about @@ -79,8 +80,8 @@ source-repository-package -- END DELETE if impl(ghc >= 9.1) - -- ekg packagess are old and unmaintained, but we - -- don't rely on them for the mainline build, so + -- ekg packagess are old and unmaintained, but we + -- don't rely on them for the mainline build, so -- this is okay allow-newer: ekg-json:base, diff --git a/flake.nix b/flake.nix index f0f92aa476..4e282cc2be 100644 --- a/flake.nix +++ b/flake.nix @@ -69,7 +69,7 @@ (pkgs.haskell.lib.justStaticExecutables (pkgs.haskell.lib.dontCheck pkgs.haskellPackages.opentelemetry-extra)) capstone # ormolu - # stylish-haskell + stylish-haskell pre-commit ] ++ lib.optionals (!stdenv.isDarwin) [ # tracy has a build problem on macos. diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 6e7e9d5831..9ac8d5487d 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -164,6 +164,11 @@ flag overloadedRecordDot default: True manual: True +flag semanticTokens + description: Enable semantic tokens plugin + default: True + manual: True + -- formatters flag floskell @@ -333,6 +338,12 @@ common refactor build-depends: hls-refactor-plugin == 2.5.0.0 cpp-options: -Dhls_refactor +common semanticTokens + if flag(semanticTokens) + build-depends: hls-semantic-tokens-plugin == 2.4.0.0 + cpp-options: -Dhls_semanticTokens + + library import: common-deps -- configuration @@ -365,6 +376,7 @@ library , stylishHaskell , refactor , overloadedRecordDot + , semanticTokens exposed-modules: Ide.Arguments diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index ab9f30f611..4c5ce21b9a 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -497,6 +497,12 @@ instance PluginMethod Request Method_TextDocumentFormatting where uri = msgParams ^. L.textDocument . L.uri pid = pluginId pluginDesc +instance PluginMethod Request Method_TextDocumentSemanticTokensFull where + pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc + && pluginEnabledConfig plcCallHierarchyOn (configForPlugin conf pluginDesc) + where + uri = msgParams ^. L.textDocument . L.uri + instance PluginMethod Request Method_TextDocumentRangeFormatting where pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc && (PluginId (formattingProvider conf) == pid || PluginId (cabalFormattingProvider conf) == pid) @@ -691,6 +697,9 @@ instance PluginRequestMethod Method_CallHierarchyOutgoingCalls where instance PluginRequestMethod (Method_CustomMethod m) where combineResponses _ _ _ _ (x :| _) = x +instance PluginRequestMethod Method_TextDocumentSemanticTokensFull where + combineResponses _ _ _ _ (x :| _) = x + takeLefts :: [a |? b] -> [a] takeLefts = mapMaybe (\x -> [res | (InL res) <- Just x]) diff --git a/plugins/hls-semantic-tokens-plugin/LICENSE b/plugins/hls-semantic-tokens-plugin/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal new file mode 100644 index 0000000000..e0f7a4fc3d --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal @@ -0,0 +1,77 @@ +cabal-version: 2.4 +name: hls-semantic-tokens-plugin +version: 2.4.0.0 +synopsis: Call hierarchy plugin for Haskell Language Server +description: + Please see the README on GitHub at + +license: Apache-2.0 +license-file: LICENSE +author: Patrick Wales +maintainer: patrickwalesss@gmail.com +category: Development +build-type: Simple +extra-source-files: + LICENSE + test/testdata/*.hs + +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + +library + buildable: True + exposed-modules: Ide.Plugin.SemanticTokens + other-modules: + Ide.Plugin.SemanticTokens.Query + Ide.Plugin.SemanticTokens.Internal + Ide.Plugin.SemanticTokens.Types + + hs-source-dirs: src + build-depends: + , aeson + , base >=4.12 && <5 + , containers + , extra + , hiedb + , ghcide == 2.4.0.0 + , hls-plugin-api == 2.4.0.0 + , lens + , lsp >=2.3 + , sqlite-simple + , text + , unordered-containers + , transformers + , bytestring + + , syb + + default-language: Haskell2010 + default-extensions: DataKinds + +test-suite tests + type: exitcode-stdio-1.0 + default-language: Haskell2010 + hs-source-dirs: test + main-is: Main.hs + ghc-options: -threaded -rtsopts -with-rtsopts=-N + + build-depends: + , aeson + , base + , containers + , extra + , filepath + , hls-semantic-tokens-plugin + , hls-test-utils == 2.4.0.0 + , ghcide-test-utils + , hls-plugin-api + , lens + , lsp + , ghc + , lsp-test + , text + , data-default + , bytestring + , ghcide == 2.4.0.0 + , hls-plugin-api == 2.4.0.0 diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs new file mode 100644 index 0000000000..54a86858bc --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -0,0 +1,14 @@ +module Ide.Plugin.SemanticTokens (descriptor, Internal.recoverSemanticTokens) where + +import Development.IDE +import qualified Ide.Plugin.SemanticTokens.Internal as Internal +import Ide.Types +import Language.LSP.Protocol.Message + + + +descriptor :: PluginId -> PluginDescriptor IdeState +descriptor plId = (defaultPluginDescriptor plId) + { Ide.Types.pluginHandlers = + mkPluginHandler SMethod_TextDocumentSemanticTokensFull Internal.semanticTokensFull + } diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs new file mode 100644 index 0000000000..20b035b5b4 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -0,0 +1,315 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# OPTIONS_GHC -Wno-orphans #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeApplications #-} + +module Ide.Plugin.SemanticTokens.Internal where + +import Control.Lens ((^.)) +import Control.Monad.IO.Class (MonadIO, liftIO) +import Control.Monad.Trans.Maybe (MaybeT (..)) +import Data.Data (Data) +import Data.Generics (everything, mkQ) +import Data.List (sortBy) +import qualified Data.List as List +import Data.Maybe (listToMaybe, mapMaybe) +import Development.IDE (Action, GetHieAst (GetHieAst), + HieAstResult (HAR, hieAst), + IdeState, + TypeCheck (TypeCheck), + realSpan) +import Development.IDE.Core.Compile (TcModuleResult (..)) +import Development.IDE.Core.Rules (getSourceFileSource, + runAction) +import Development.IDE.Core.Shake (use) +import Development.IDE.GHC.Compat +import Ide.Plugin.Error (getNormalizedFilePathE) +import qualified Language.LSP.Protocol.Lens as L +import Language.LSP.Protocol.Message +import Language.LSP.Protocol.Types +-- import Language.LSP.Protocol.Types.Common +import qualified Data.Text as T +import Ide.Plugin.SemanticTokens.Types +import Ide.Types +-- import System.FilePath (takeExtension) +import Control.Monad.Trans.Class (lift) +import Data.ByteString (ByteString, unpack) +import Data.Generics (Typeable) +import Data.List.Extra (chunksOf) +import qualified Data.List.NonEmpty as NonEmpty +import qualified Data.Map as Map +import qualified Data.Set as Set +import Data.Typeable (cast) +import Development.IDE (IdeState, Priority (..), + ideLogger, logPriority) + +logWith :: (Show a, MonadIO m) => IdeState -> a -> m () +logWith st = liftIO . logPriority (ideLogger st) Info . T.pack . show + + +deriving instance Show DeclType +deriving instance Show BindType +deriving instance Show RecFieldContext +----------------------- +---- the api +----------------------- +instance Show ContextInfo where + show x = case x of + Use -> "Use" + MatchBind -> "MatchBind" + IEThing _ -> "IEThing IEType" -- imported + TyDecl -> "TyDecl" + ValBind bt _ _ -> "ValBind of " <> show bt + PatternBind _ _ _ -> "PatternBind" + ClassTyDecl _ -> "ClassTyDecl" + Decl d _ -> "Decl of " <> show d + TyVarBind _ _ -> "TyVarBind" + RecField c _ -> "RecField of " <> show c + EvidenceVarBind _ _ _ -> "EvidenceVarBind" + EvidenceVarUse -> "EvidenceVarUse" + +instance Show (IdentifierDetails a) where + show x = show $ identInfo x + + +getOriginalText :: String -> Span -> String +getOriginalText sourceCode span = take len $ drop (startChar-1) tLine + where tLine = lines sourceCode !! (line-1) + line = srcSpanStartLine span + startChar = srcSpanStartCol span + len= srcSpanEndCol span - startChar + +bytestringString :: ByteString -> String +bytestringString = map (toEnum . fromEnum) . unpack +-- para :: MessageParams 'Method_TextDocumentSemanticTokensFull +-- para = _ +computeSemanticTokens :: NormalizedFilePath -> Action (Maybe SemanticTokens) +computeSemanticTokens nfp = runMaybeT $ do + -- HAR{hieAst} <- MaybeT $ use GetHieAst nfp + -- source :: ByteString <- lift $ getSourceFileSource nfp + + -- let xs = Map.toList $ getAsts hieAst + -- liftIO $ putStrLn $ "size" <> show (List.length xs) + -- -- typedAst <- MaybeT $ pure $ cast hieAst + -- case xs of + -- (x:_) -> do + -- liftIO $ putStrLn $ "computeSemanticTokens': " <> show (fst x) + -- MaybeT $ computeSemanticTokens' (bytestringString source) $ snd x + -- _ -> pure () + + tcM <- MaybeT $ use TypeCheck nfp + pure $ sourceToTokens $ tmrRenamed tcM + +computeSemanticTokens' :: forall a . String -> HieAST a -> Action (Maybe ()) +computeSemanticTokens' src hieAst = do + let identifiers = map NIdentifier $ identifierGetter hieAst + liftIO $ mapM_ (\x -> putStrLn $ getOriginalTextFromId src x <> ":" <> show x) identifiers + -- liftIO $ print $ "identifiers size: " <> show ( identifiers) + pure $ Just () + + + +semanticTokensFull :: PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull +semanticTokensFull state _ param = do + let dbg = logWith state + nfp <- getNormalizedFilePathE (param ^. (L.textDocument . L.uri)) + dbg $ "semanticTokensFull: " <> show nfp + items <- liftIO + $ runAction "SemanticTokens.semanticTokensFull" state + $ computeSemanticTokens nfp + case items of + Nothing -> pure $ InR Null + Just items -> do + content <- liftIO $ readFile $ fromNormalizedFilePath nfp + -- dbg $ unlines $ map show $ recoverSemanticTokens content items + pure $ InL items + +----------------------- +---- convert to lsp +----------------------- + +recoverSemanticTokens :: String -> SemanticTokens -> [SemanticTokenOriginal] +recoverSemanticTokens sourceCode (SemanticTokens _ xs) = map (tokenOrigin sourceCode) $ recoverSemanticToken xs + +tokenOrigin :: String -> SemanticTokenData -> SemanticTokenOriginal +tokenOrigin sourceCode (line, startChar, len, tokenType, _) = SemanticTokenOriginal (toEnum tokenType) (Loc line startChar len) name + where tLine = lines sourceCode !! (line-1) + name = take len $ drop (startChar-0) tLine + + +-- every five elements is a token +recoverSemanticToken :: [UInt] -> [SemanticTokenData] +recoverSemanticToken xs = + recoverPosition $ + if length xs `mod` 5 /= 0 + then panic "recoverSemanticToken: impossible" + else map toTuple $ chunksOf 5 $ map fromIntegral $ xs + where toTuple [a, b, c, d, e] = (a, b, c, d, e) + toTuple _ = panic "recoverSemanticToken: impossible" + -- recover to absolute position + recoverPosition :: [SemanticTokenData] -> [SemanticTokenData] + recoverPosition xs = ls $ foldl f (1, 0, []) xs + where + f (lastLine, lastStartChar, acc) (line, startChar, len, tokenType, tokenModifiers) + = let newStartChar = if line == 0 then startChar + lastStartChar else startChar + newline = line + lastLine + in + (newline,newStartChar, + (newline,newStartChar, len, tokenType, tokenModifiers) :acc) + ls (_, _, acc) = List.reverse acc + + + +sourceToTokens :: RenamedSource -> SemanticTokens +sourceToTokens = toLspSemanticTokens . toSemanticTokens . nameGetter + +emptySemanticTokens :: SemanticTokens +emptySemanticTokens = SemanticTokens Nothing [] + +toLspSemanticTokens :: [SemanticToken] -> SemanticTokens +toLspSemanticTokens xs = SemanticTokens Nothing (concatMap toTokenInt xs) + + +-- semanticTokenToString :: SemanticToken -> String +-- semanticTokenToString ((line, startChar, len, tokenType, tokenModifiers), locName) = +-- show line ++ ":" ++ show startChar ++ ":" ++ show len ++ ":" ++ show tokenType ++ ":" ++ show tokenModifiers ++ ":\n" ++ collectToString locName + +toTokenInt :: SemanticToken -> [UInt] +toTokenInt ((line, startChar, len, tokenType, tokenModifiers), _) = + [fromIntegral line, fromIntegral startChar, fromIntegral len, fromIntegral tokenType, fromIntegral tokenModifiers] + +-- need to take offset +toSemanticTokens :: [SemanticCollectFinal] -> [SemanticToken] +toSemanticTokens = computeOffsets . List.sortOn fst. mapMaybe toSemanticToken + where + computeOffsets :: [SemanticToken] -> [SemanticToken] + computeOffsets = ls . foldl f (1, 0, []) + f (lastLine, lastStartChar, acc) ((line, startChar, len, tokenType, tokenModifiers), locName) + = ( line, startChar, + (if lastLine == line then + (0, startChar - lastStartChar, len, tokenType, tokenModifiers) + else + (line - lastLine, startChar, len, tokenType, tokenModifiers) + , locName) :acc) + ls (_, _, acc) = List.reverse acc + +toSemanticToken :: SemanticCollectFinal -> Maybe SemanticToken +toSemanticToken ori@(tokenType, locName) = do + loc <- realSpan $ getLocA locName + let line = srcSpanStartLine loc + let startChar = srcSpanStartCol loc + let len= srcSpanEndCol loc - startChar + return + -- vscode render col start from 0 + ((line, startChar-1, len, fromEnum tokenType, 0), ori) + + +----------------------- +---- collect from ast +----------------------- + + +toTokenType :: LIdP GhcRn -> SemanticTokenType +toTokenType locName = case occNameSpace $ occName $ unLoc locName of + x | isTcClsNameSpace x -> SClass + x | isTvNameSpace x -> STypeVar + x | isDataConNameSpace x -> SDataCon + x | isVarNameSpace x -> SVariable + _ -> SNothing + +nameToCollect :: LIdP GhcRn -> [SemanticCollectFinal] +nameToCollect locName = [(toTokenType locName, locName)] + +nameGetter :: RenamedSource -> [SemanticCollectFinal] +nameGetter = everything (++) ([] `mkQ` nameToCollect) + + + +-- identifier from source +-- isSrcIdentifier :: Identifier -> Bool + +-- filter generated identifiers + +-- from declaration site to token type +type TokenTypeMap = Map.Map Span SemanticTokenTypeI +type TokenTypeItem = (Span, SemanticTokenTypeI) +type IdentifierItem = (Span, Maybe SrcSpan, Identifier, [ContextInfo]) + + +newtype NIdentifier = NIdentifier IdentifierItem + +instance Show NIdentifier where + show (NIdentifier (span, c,Left x, y)) = "module:" <> show x <> " " <> show y <> " " <> printCompactRealSrc span + show (NIdentifier (span, c, Right x, y)) = show (nameOccName x) <> " " <> show y <> "" + <> maybe "" printCompactSrcSpan c <> " " <> printCompactRealSrc span + +addToTokenTypeMap :: IdentifierItem -> Maybe TokenTypeItem +addToTokenTypeMap (_, Nothing, _, _) = Nothing +addToTokenTypeMap (span, _ , _, contexts) + | Just ctx <- declInfo contexts + = case ctx of + Decl ClassDec _ -> Just (span, TClass) + Decl DataDec _ -> Just (span, TDataCon) + Decl ConDec _ -> Just (span, TTypeCon) + Decl SynDec _ -> Just (span, TTypeSyn) + Decl FamDec _ -> Just (span, TTypeFam) + -- nothing for instance dec + Decl InstDec span -> Nothing + -- todo pattern syn + Decl PatSynDec span -> Nothing + -- | Just ctx <- valBindInfo contexts + -- = case ctx of + -- ValBind FunBind _ _ -> Just (span, TFunction) + -- ValBind PatBind _ _ -> Just (span, TVariable) + -- ValBind VarBind _ _ -> Just (span, TVariable) + -- ValBind PatSynBind _ _ -> Nothing + | otherwise = Nothing + + +recFieldInfo, declInfo, valBindInfo, classTyDeclInfo, + useInfo, patternBindInfo, tyDeclInfo, matchBindInfo :: [ContextInfo] -> Maybe ContextInfo +recFieldInfo ctxs = listToMaybe [ctx | ctx@RecField{} <- ctxs] +declInfo ctxs = listToMaybe [ctx | ctx@Decl{} <- ctxs] +valBindInfo ctxs = listToMaybe [ctx | ctx@ValBind{} <- ctxs] +classTyDeclInfo ctxs = listToMaybe [ctx | ctx@ClassTyDecl{} <- ctxs] +useInfo ctxs = listToMaybe [Use | Use <- ctxs] +patternBindInfo ctxs = listToMaybe [ctx | ctx@PatternBind{} <- ctxs] +tyDeclInfo ctxs = listToMaybe [TyDecl | TyDecl <- ctxs] +matchBindInfo ctxs = listToMaybe [MatchBind | MatchBind <- ctxs] + + + + +-- | Recurses through the given AST to find identifiers which are +-- 'InstanceValBind's. +identifierGetter :: HieAST a -> [IdentifierItem] +identifierGetter ast = + let ids = [ (nodeSpan ast, getIdSrcSpan c, c, Set.toList $ identInfo d) | (c, d) <- Map.toList $ getNodeIds ast] + in ids <> concatMap identifierGetter (nodeChildren ast) + where getIdSrcSpan :: Identifier -> Maybe SrcSpan + getIdSrcSpan (Left _) = Nothing + getIdSrcSpan (Right x) = Just $ nameSrcSpan x + +printCompactSrcSpan :: SrcSpan -> String +printCompactSrcSpan (RealSrcSpan x _buf) = printCompactRealSrc x +printCompactSrcSpan x = "noSrc" + +printCompactRealSrc :: RealSrcSpan -> String +printCompactRealSrc x = showSDocUnsafe $ ppr x + + + +getOriginalTextFromId :: String -> NIdentifier -> String +getOriginalTextFromId sourceCode (NIdentifier (span, c, _, _)) = getOriginalText sourceCode span + +-- identifierGetter :: Data a => HieAST a -> [(Identifier, IdentifierDetails a)] +-- identifierGetter = everything (++) ([] `mkQ` identifierToCollect) + +-- identifierToCollect :: Data a => NodeIdentifiers a -> [(Identifier, IdentifierDetails a)] +-- identifierToCollect xs = Map.toList xs diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs new file mode 100644 index 0000000000..939e3ff239 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -0,0 +1,112 @@ +module Ide.Plugin.SemanticTokens.Types where + + +import Development.IDE.GHC.Compat + +data SemanticTokenType = SClass | SVariable | STypeVar | SDataCon | SNothing | SFieldName deriving (Eq, Ord) +-- +data SemanticTokenTypeI = + TVariable -- Original variable, can be override The following + -- by match + | TPatternBind -- PatternBind, parameters, as values in let/in, case, lambda + | TMatchBind -- MatchBind, valBind instance bind or regular bind + | TRecField -- from match bind + -- by decls + | TTypeSyn -- Type synonym (Non-local is not captured) + | TClass -- Class (Constraint constructor) + | TTypeCon -- Type (Type constructor) + | TDataCon -- Data constructor + | TTypeFam -- type family + deriving (Eq, Ord) + +instance Show SemanticTokenType where + show SClass = "class" + show SVariable = "SVariable" + show STypeVar = "STypeVar" + show SDataCon = "SDataCon" + show SFieldName = "SFieldName" + show SNothing = "SNothing" + + + + -- capability.tokenTypes = [ + -- SemanticTokenTypes.namespace, + -- SemanticTokenTypes.type, + -- SemanticTokenTypes.class, + -- SemanticTokenTypes.enum, + -- SemanticTokenTypes.interface, + -- SemanticTokenTypes.struct, + -- SemanticTokenTypes.typeParameter, + -- SemanticTokenTypes.parameter, + -- SemanticTokenTypes.variable, + -- SemanticTokenTypes.property, + -- SemanticTokenTypes.enumMember, + -- SemanticTokenTypes.event, + -- SemanticTokenTypes.function, + -- SemanticTokenTypes.method, + -- SemanticTokenTypes.macro, + -- SemanticTokenTypes.keyword, + -- SemanticTokenTypes.modifier, + -- SemanticTokenTypes.comment, + -- SemanticTokenTypes.string, + -- SemanticTokenTypes.number, + -- SemanticTokenTypes.regexp, + -- SemanticTokenTypes.operator, + -- SemanticTokenTypes.decorator + -- ]; + + -- capability.tokenModifiers = [ + -- SemanticTokenModifiers.declaration, + -- SemanticTokenModifiers.definition, + -- SemanticTokenModifiers.readonly, + -- SemanticTokenModifiers.static, + -- SemanticTokenModifiers.deprecated, + -- SemanticTokenModifiers.abstract, + -- SemanticTokenModifiers.async, + -- SemanticTokenModifiers.modification, + -- SemanticTokenModifiers.documentation, + -- SemanticTokenModifiers.defaultLibrary + -- ]; +classToken = 2 +variableToken = 8 +-- enumToken = 4 +typeParameter = 6 +enumMember = 10 +property = 9 + + +instance Enum SemanticTokenType where + toEnum 2 = SClass -- class + toEnum 8 = SVariable -- variable + toEnum 6 = STypeVar -- typeParameter + toEnum 10 = SDataCon -- enumMember + toEnum 9 = SFieldName -- property + toEnum 14 = SNothing -- comment + toEnum x = error $ "SemanticTokenType.toEnum: bad argument:" ++ show x + + fromEnum SClass = classToken + fromEnum SVariable = variableToken + fromEnum STypeVar = typeParameter + fromEnum SDataCon = enumMember + fromEnum SFieldName = property + fromEnum SNothing = 14 + +type SemanticCollectFinal = (SemanticTokenType, LIdP GhcRn) +-- { line: 2, startChar 5, length: 3, tokenType: SemanticTokenType, tokenModifiers: 3, string}, +type SemanticToken = (SemanticTokenData, SemanticCollectFinal) +type SemanticTokenData = (Int, Int, Int, Int, Int) +type SemanticTokenInt = Int + +data SemanticTokenOriginal = SemanticTokenOriginal + { tokenType :: SemanticTokenType, + loc :: Loc, + name :: String + } + deriving (Show, Eq, Ord) + +data Loc = Loc + { line :: Int, + startChar :: Int, + len :: Int + } + deriving (Show, Eq, Ord) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs new file mode 100644 index 0000000000..e8721bd30f --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs @@ -0,0 +1,15 @@ +module Ide.Plugin.SemanticTokens.Query where +import Data.Map (Map) + + +-- type IdentifierMap = Map (Set ContextInfo) + +-- | Recurses through the given AST to find identifiers which are +-- 'InstanceValBind's. +-- identifierGetter :: HieAST a -> [(Span, Identifier, IdentifierDetails a)] +-- identifierGetter ast = +-- let ids = [ (nodeSpan ast, c, d) | (c, d) <- Map.toList $ getNodeIds ast] +-- in ids <> concatMap identifierGetter (nodeChildren ast) + + + diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs new file mode 100644 index 0000000000..02e1d4e542 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -0,0 +1,171 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeApplications #-} + +import Data.Bifunctor +import Data.ByteString as BS +import Data.Default +import qualified Data.Maybe +import Data.Text +import Development.IDE.GHC.Compat +-- import GHC.Data.StringBuffer +-- import GHC.Driver.Config.Parser +-- import GHC.Hs +-- import GHC.Hs.Dump +-- import qualified GHC.Parser as Parser ( parseModule ) +-- import GHC.Parser.Lexer +-- import GHC.Platform +-- import GHC.Plugins +-- import GHC.Settings +-- import GHC.Settings.Config +-- import GHC.Generics +-- import Data.Generics (everything, mkQ, extQ) +-- import GHC +-- import qualified GHC +-- import GHC.Paths (libdir) +-- import GHC.Tc.Types +-- import GHC.Tc.Utils.Env (lookupGlobal) +import Development.IDE +-- import Development.IDE.Test +-- import Test.Hls +-- import Test.Hls.Util (withCanonicalTempDir) +import Control.Arrow (Arrow ((***)), (&&&), (+++)) +import Data.Data +import Data.Functor (void) +import Data.Map as Map +import Data.String (fromString) +import System.Environment.Blank +import System.FilePath +-- import qualified Development.IDE.Core.Shake as Shake +import Ide.Types +import qualified Test.Hls (PluginTestDescriptor, + mkPluginTestDescriptor', + runSessionWithServerInTmpDir, + waitForAction) +import qualified Test.Hls.FileSystem as FS +-- import Development.IDE.Plugin.Test +import Control.Lens hiding (use) +import qualified Data.List as List +import Data.Maybe (fromJust) +import qualified Data.Set as Set +import Ide.Plugin.Error (getNormalizedFilePathE) +import Ide.Plugin.SemanticTokens +import qualified Language.LSP.Protocol.Lens as L +import Language.LSP.Protocol.Types +import Language.LSP.Protocol.Types (SemanticTokensParams (..)) +import qualified Language.LSP.Test as Test +import Test.Hls +import Test.Hls (TextDocumentIdentifier, + getCodeLenses, openDoc, + waitForAction) +import Test.Hls.Util (withCanonicalTempDir) + +getUniqueName :: (NamedThing a) => a -> Name +getUniqueName = getName + +-- astToString :: (Data a) => a -> String +-- astToString = showSDoc fakeDynFlags . showAstDataFull + +testDataDir :: FilePath +testDataDir = "test" "testdata" + +mkFs :: [FS.FileTree] -> FS.VirtualFileTree +mkFs = FS.mkVirtualFileTree testDataDir + +semanticTokensPlugin :: Test.Hls.PluginTestDescriptor () +semanticTokensPlugin = Test.Hls.mkPluginTestDescriptor' Ide.Plugin.SemanticTokens.descriptor "SemanticTokens" + + +mkSemanticTokensParams :: TextDocumentIdentifier -> SemanticTokensParams +mkSemanticTokensParams doc = SemanticTokensParams Nothing Nothing doc + +main1 :: IO () +main1 = do + + let filePath = "./test/testdata/T1.hs" + content <- Prelude.readFile filePath + Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject "T1.hs") $ do + doc <- openDoc "T1.hs" "haskell" + res <- waitForAction "TypeCheck" doc + -- res <- waitForAction "SemanticTokens.semanticTokensFull" doc + res <- Test.getSemanticTokens doc + case res ^? _L of + Just tokens -> liftIO $ mapM_ print $ recoverSemanticTokens content tokens + _ -> liftIO $ print "error" + liftIO $ print res + lenses <- getCodeLenses doc + liftIO $ print lenses + + -- let filePath = "hello.hs" + -- content <- Prelude.readFile filePath + -- runGhc (Just libdir) $ do + -- dflags <- getSessionDynFlags + -- setSessionDynFlags dflags + -- target <- guessTarget filePath Nothing Nothing + -- setTargets [target] + -- load LoadAllTargets + -- -- hsc_typecheck :: Bool -- ^ Keep renamed source? + -- -- -> ModSummary -> Maybe HsParsedModule + -- -- -> Hsc (TcGblEnv, RenamedStuff) + -- modSum <- getModSummary $ mkModuleName "Main" + -- parsedModule <- parseModule modSum + -- typecheckedModule <- typecheckModule parsedModule + -- let rms = fromJust $ renamedSource typecheckedModule + -- let tcGblEnv = fst $ tm_internals_ typecheckedModule + -- -- mkHieFileWithSource :: FilePath + -- -- -> BS.ByteString + -- -- -> ModSummary + -- -- -> TcGblEnv + -- -- -> RenamedSource -> HieFile + -- -- let file = mkHieFileWithSource filePath content modSum tcGblEnv rms + -- file <- mkHieFile modSum tcGblEnv rms + -- let ast = snd $ List.head $ Map.toList $ getAsts $ hie_asts file + -- let fileContent = srcSpanFile $ nodeSpan ast + -- liftIO $ print fileContent + -- let x = getter content ast + -- -- liftIO $ mapM_ print x + -- liftIO $ Prelude.writeFile "out.txt" $ Prelude.unlines $ fmap show x + + -- let mm = getSourcedNodeInfo ast + + -- liftIO $ Prelude.writeFile "out2.txt" $ showSDoc fakeDynFlags $ ppr ast + -- liftIO $ Prelude.writeFile "out3.txt" $ astToString $ tm_renamed_source typecheckedModule + -- liftIO $ Prelude.writeFile "out4.txt" $ Prelude.unlines (collectToString <$> nameGetter rms) + -- liftIO $ Prelude.writeFile "out5.txt" $ Prelude.unlines $ fmap semanticTokenToString $ toSemanticTokens $ nameGetter rms + + -- liftIO $ print $ Map.size $ nodeIdentifiers $ snd $ List.head $ Map.toList mm + + -- liftIO $ mapM (print . (astToString &&& (show . nameUnique) &&& (show . nameSrcSpan))) names + return () + +semanticTokensTests :: TestTree +semanticTokensTests = + testGroup + "get semantic Tokens" + [ testCase "variable" $ do + let filePath = "./test/testdata/T1.hs" + content <- Prelude.readFile filePath + Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject "T1.hs") $ do + doc <- openDoc "T1.hs" "haskell" + res <- waitForAction "TypeCheck" doc + -- res <- waitForAction "SemanticTokens.semanticTokensFull" doc + res <- Test.getSemanticTokens doc + case res ^? _L of + Just tokens -> liftIO $ mapM_ print $ recoverSemanticTokens content tokens + _ -> liftIO $ print "error" + liftIO $ print res + lenses <- getCodeLenses doc + liftIO $ print lenses + ] + +main :: IO () +main = defaultTestRunner $ + testGroup "Semantic tokens" + [ semanticTokensTests ] diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/T1.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/T1.hs new file mode 100644 index 0000000000..cfd8c09b72 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/T1.hs @@ -0,0 +1,41 @@ +module Main where + +import Data.Set (Set, insert) + + +data Foo = Foo { foo :: Int } + +class Boo a where + boo :: a -> a + +instance Boo Int where + boo x = x + 1 + +data Dd = Dd Int + +data Doo = Doo Prelude.Int +type Bar1 = Int +type Bar2 = Doo + +bb :: (Boo a) => a -> a +bb x = boo x +aa :: cool -> Int -> cool +aa x = \c -> aa x c + where (xx, yy) = (1, 2) + dd = 1 + +(zz, kk) = (1, 2) +cc :: Foo -> (Int, Int) -> Int +cc f (gg, vv)= + case gg of + 1 -> foo $ f { foo = 1 } + 2 -> foo $ f { foo = 1 } + +go = foo +add = (Prelude.+) + +-- sub :: Int -> Int -> Int +-- sub x y = add x y + +main :: IO () +main = putStrLn "Hello, Haskell!" diff --git a/src/HlsPlugins.hs b/src/HlsPlugins.hs index 4d37185998..8ab78900c8 100644 --- a/src/HlsPlugins.hs +++ b/src/HlsPlugins.hs @@ -120,6 +120,11 @@ import qualified Ide.Plugin.StylishHaskell as StylishHaskell import qualified Development.IDE.Plugin.CodeAction as Refactor #endif +#if hls_semanticTokens +import qualified Ide.Plugin.SemanticTokens as SemanticTokens +#endif + + data Log = forall a. (Pretty a) => Log PluginId a instance Pretty Log where @@ -172,6 +177,9 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins #if hls_callHierarchy CallHierarchy.descriptor "callHierarchy" : #endif +#if hls_semanticTokens + SemanticTokens.descriptor "semanticTokens" : +#endif #if hls_class let pId = "class" in Class.descriptor (pluginRecorder pId) pId: #endif diff --git a/stack.yaml b/stack.yaml index 922b55f461..ed7cdf8ad2 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,4 +1,4 @@ -resolver: nightly-2023-07-10 # ghc-9.6.2 +resolver: nightly-2023-12-06 # ghc-9.6.2 packages: - . @@ -35,6 +35,7 @@ packages: - ./plugins/hls-splice-plugin - ./plugins/hls-stan-plugin - ./plugins/hls-stylish-haskell-plugin + - ./plugins/hls-semantic-tokens-plugin ghc-options: "$everything": -haddock From 42d910986e38b9ea0213a34f178638821956e269 Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 7 Dec 2023 22:02:43 +0800 Subject: [PATCH 02/73] SemanticTokens: combine information extracted from HieAst --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 193 ++-------------- .../src/Ide/Plugin/SemanticTokens/Types.hs | 73 +++--- .../src/Ide/Plugin/SemanticTokens/query.hs | 213 +++++++++++++++++- .../test/testdata/T1.hs | 7 + 4 files changed, 271 insertions(+), 215 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 20b035b5b4..763191e8bc 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -6,7 +6,6 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-} module Ide.Plugin.SemanticTokens.Internal where @@ -35,9 +34,11 @@ import Language.LSP.Protocol.Message import Language.LSP.Protocol.Types -- import Language.LSP.Protocol.Types.Common import qualified Data.Text as T +import Ide.Plugin.SemanticTokens.Query import Ide.Plugin.SemanticTokens.Types import Ide.Types -- import System.FilePath (takeExtension) +import Control.Arrow ((&&&), (+++)) import Control.Monad.Trans.Class (lift) import Data.ByteString (ByteString, unpack) import Data.Generics (Typeable) @@ -53,63 +54,36 @@ logWith :: (Show a, MonadIO m) => IdeState -> a -> m () logWith st = liftIO . logPriority (ideLogger st) Info . T.pack . show -deriving instance Show DeclType -deriving instance Show BindType -deriving instance Show RecFieldContext ----------------------- ---- the api ----------------------- -instance Show ContextInfo where - show x = case x of - Use -> "Use" - MatchBind -> "MatchBind" - IEThing _ -> "IEThing IEType" -- imported - TyDecl -> "TyDecl" - ValBind bt _ _ -> "ValBind of " <> show bt - PatternBind _ _ _ -> "PatternBind" - ClassTyDecl _ -> "ClassTyDecl" - Decl d _ -> "Decl of " <> show d - TyVarBind _ _ -> "TyVarBind" - RecField c _ -> "RecField of " <> show c - EvidenceVarBind _ _ _ -> "EvidenceVarBind" - EvidenceVarUse -> "EvidenceVarUse" - -instance Show (IdentifierDetails a) where - show x = show $ identInfo x - - -getOriginalText :: String -> Span -> String -getOriginalText sourceCode span = take len $ drop (startChar-1) tLine - where tLine = lines sourceCode !! (line-1) - line = srcSpanStartLine span - startChar = srcSpanStartCol span - len= srcSpanEndCol span - startChar - bytestringString :: ByteString -> String bytestringString = map (toEnum . fromEnum) . unpack -- para :: MessageParams 'Method_TextDocumentSemanticTokensFull -- para = _ computeSemanticTokens :: NormalizedFilePath -> Action (Maybe SemanticTokens) computeSemanticTokens nfp = runMaybeT $ do - -- HAR{hieAst} <- MaybeT $ use GetHieAst nfp - -- source :: ByteString <- lift $ getSourceFileSource nfp + HAR{hieAst} <- MaybeT $ use GetHieAst nfp + source :: ByteString <- lift $ getSourceFileSource nfp - -- let xs = Map.toList $ getAsts hieAst - -- liftIO $ putStrLn $ "size" <> show (List.length xs) - -- -- typedAst <- MaybeT $ pure $ cast hieAst - -- case xs of - -- (x:_) -> do - -- liftIO $ putStrLn $ "computeSemanticTokens': " <> show (fst x) - -- MaybeT $ computeSemanticTokens' (bytestringString source) $ snd x - -- _ -> pure () + let xs = Map.toList $ getAsts hieAst + liftIO $ putStrLn $ "size" <> show (List.length xs) + -- typedAst <- MaybeT $ pure $ cast hieAst + case xs of + (x:_) -> do + liftIO $ putStrLn $ "computeSemanticTokens': " <> show (fst x) + MaybeT $ computeSemanticTokens' (bytestringString source) $ snd x + tcM <- MaybeT $ use TypeCheck nfp + pure $ toLspSemanticTokens $ extractSemanticTokens (snd x) $ tmrRenamed tcM + _ -> MaybeT . pure $ Nothing - tcM <- MaybeT $ use TypeCheck nfp - pure $ sourceToTokens $ tmrRenamed tcM computeSemanticTokens' :: forall a . String -> HieAST a -> Action (Maybe ()) computeSemanticTokens' src hieAst = do - let identifiers = map NIdentifier $ identifierGetter hieAst - liftIO $ mapM_ (\x -> putStrLn $ getOriginalTextFromId src x <> ":" <> show x) identifiers + -- let identifiers = map NIdentifier $ identifierGetter hieAst + let identifiersGroups = (map .map) NIdentifier $ toNameGroups $ identifierGetter hieAst + + liftIO $ mapM_ (\gr -> liftIO (putStrLn $ "group size: " <> show (List.length gr)) >> mapM_ (\x -> putStrLn $ getOriginalTextFromId src x <> ":" <> show x) gr) identifiersGroups -- liftIO $ print $ "identifiers size: " <> show ( identifiers) pure $ Just () @@ -165,7 +139,6 @@ recoverSemanticToken xs = ls (_, _, acc) = List.reverse acc - sourceToTokens :: RenamedSource -> SemanticTokens sourceToTokens = toLspSemanticTokens . toSemanticTokens . nameGetter @@ -183,133 +156,3 @@ toLspSemanticTokens xs = SemanticTokens Nothing (concatMap toTokenInt xs) toTokenInt :: SemanticToken -> [UInt] toTokenInt ((line, startChar, len, tokenType, tokenModifiers), _) = [fromIntegral line, fromIntegral startChar, fromIntegral len, fromIntegral tokenType, fromIntegral tokenModifiers] - --- need to take offset -toSemanticTokens :: [SemanticCollectFinal] -> [SemanticToken] -toSemanticTokens = computeOffsets . List.sortOn fst. mapMaybe toSemanticToken - where - computeOffsets :: [SemanticToken] -> [SemanticToken] - computeOffsets = ls . foldl f (1, 0, []) - f (lastLine, lastStartChar, acc) ((line, startChar, len, tokenType, tokenModifiers), locName) - = ( line, startChar, - (if lastLine == line then - (0, startChar - lastStartChar, len, tokenType, tokenModifiers) - else - (line - lastLine, startChar, len, tokenType, tokenModifiers) - , locName) :acc) - ls (_, _, acc) = List.reverse acc - -toSemanticToken :: SemanticCollectFinal -> Maybe SemanticToken -toSemanticToken ori@(tokenType, locName) = do - loc <- realSpan $ getLocA locName - let line = srcSpanStartLine loc - let startChar = srcSpanStartCol loc - let len= srcSpanEndCol loc - startChar - return - -- vscode render col start from 0 - ((line, startChar-1, len, fromEnum tokenType, 0), ori) - - ------------------------ ----- collect from ast ------------------------ - - -toTokenType :: LIdP GhcRn -> SemanticTokenType -toTokenType locName = case occNameSpace $ occName $ unLoc locName of - x | isTcClsNameSpace x -> SClass - x | isTvNameSpace x -> STypeVar - x | isDataConNameSpace x -> SDataCon - x | isVarNameSpace x -> SVariable - _ -> SNothing - -nameToCollect :: LIdP GhcRn -> [SemanticCollectFinal] -nameToCollect locName = [(toTokenType locName, locName)] - -nameGetter :: RenamedSource -> [SemanticCollectFinal] -nameGetter = everything (++) ([] `mkQ` nameToCollect) - - - --- identifier from source --- isSrcIdentifier :: Identifier -> Bool - --- filter generated identifiers - --- from declaration site to token type -type TokenTypeMap = Map.Map Span SemanticTokenTypeI -type TokenTypeItem = (Span, SemanticTokenTypeI) -type IdentifierItem = (Span, Maybe SrcSpan, Identifier, [ContextInfo]) - - -newtype NIdentifier = NIdentifier IdentifierItem - -instance Show NIdentifier where - show (NIdentifier (span, c,Left x, y)) = "module:" <> show x <> " " <> show y <> " " <> printCompactRealSrc span - show (NIdentifier (span, c, Right x, y)) = show (nameOccName x) <> " " <> show y <> "" - <> maybe "" printCompactSrcSpan c <> " " <> printCompactRealSrc span - -addToTokenTypeMap :: IdentifierItem -> Maybe TokenTypeItem -addToTokenTypeMap (_, Nothing, _, _) = Nothing -addToTokenTypeMap (span, _ , _, contexts) - | Just ctx <- declInfo contexts - = case ctx of - Decl ClassDec _ -> Just (span, TClass) - Decl DataDec _ -> Just (span, TDataCon) - Decl ConDec _ -> Just (span, TTypeCon) - Decl SynDec _ -> Just (span, TTypeSyn) - Decl FamDec _ -> Just (span, TTypeFam) - -- nothing for instance dec - Decl InstDec span -> Nothing - -- todo pattern syn - Decl PatSynDec span -> Nothing - -- | Just ctx <- valBindInfo contexts - -- = case ctx of - -- ValBind FunBind _ _ -> Just (span, TFunction) - -- ValBind PatBind _ _ -> Just (span, TVariable) - -- ValBind VarBind _ _ -> Just (span, TVariable) - -- ValBind PatSynBind _ _ -> Nothing - | otherwise = Nothing - - -recFieldInfo, declInfo, valBindInfo, classTyDeclInfo, - useInfo, patternBindInfo, tyDeclInfo, matchBindInfo :: [ContextInfo] -> Maybe ContextInfo -recFieldInfo ctxs = listToMaybe [ctx | ctx@RecField{} <- ctxs] -declInfo ctxs = listToMaybe [ctx | ctx@Decl{} <- ctxs] -valBindInfo ctxs = listToMaybe [ctx | ctx@ValBind{} <- ctxs] -classTyDeclInfo ctxs = listToMaybe [ctx | ctx@ClassTyDecl{} <- ctxs] -useInfo ctxs = listToMaybe [Use | Use <- ctxs] -patternBindInfo ctxs = listToMaybe [ctx | ctx@PatternBind{} <- ctxs] -tyDeclInfo ctxs = listToMaybe [TyDecl | TyDecl <- ctxs] -matchBindInfo ctxs = listToMaybe [MatchBind | MatchBind <- ctxs] - - - - --- | Recurses through the given AST to find identifiers which are --- 'InstanceValBind's. -identifierGetter :: HieAST a -> [IdentifierItem] -identifierGetter ast = - let ids = [ (nodeSpan ast, getIdSrcSpan c, c, Set.toList $ identInfo d) | (c, d) <- Map.toList $ getNodeIds ast] - in ids <> concatMap identifierGetter (nodeChildren ast) - where getIdSrcSpan :: Identifier -> Maybe SrcSpan - getIdSrcSpan (Left _) = Nothing - getIdSrcSpan (Right x) = Just $ nameSrcSpan x - -printCompactSrcSpan :: SrcSpan -> String -printCompactSrcSpan (RealSrcSpan x _buf) = printCompactRealSrc x -printCompactSrcSpan x = "noSrc" - -printCompactRealSrc :: RealSrcSpan -> String -printCompactRealSrc x = showSDocUnsafe $ ppr x - - - -getOriginalTextFromId :: String -> NIdentifier -> String -getOriginalTextFromId sourceCode (NIdentifier (span, c, _, _)) = getOriginalText sourceCode span - --- identifierGetter :: Data a => HieAST a -> [(Identifier, IdentifierDetails a)] --- identifierGetter = everything (++) ([] `mkQ` identifierToCollect) - --- identifierToCollect :: Data a => NodeIdentifiers a -> [(Identifier, IdentifierDetails a)] --- identifierToCollect xs = Map.toList xs diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 939e3ff239..819f0ca2d5 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -3,29 +3,34 @@ module Ide.Plugin.SemanticTokens.Types where import Development.IDE.GHC.Compat -data SemanticTokenType = SClass | SVariable | STypeVar | SDataCon | SNothing | SFieldName deriving (Eq, Ord) +-- data SemanticTokenType = SClass | SVariable | STypeVar | SDataCon | SNothing | SFieldName deriving (Eq, Ord) -- -data SemanticTokenTypeI = - TVariable -- Original variable, can be override The following +data SemanticTokenType = + TVariable -- fallback to variable for all other cases + | TTypeVariable -- Type variable -- by match | TPatternBind -- PatternBind, parameters, as values in let/in, case, lambda - | TMatchBind -- MatchBind, valBind instance bind or regular bind + | TValBind -- MatchBind, valBind instance bind or regular bind | TRecField -- from match bind + | TPatternSyn -- Pattern synonym + + | TClassMethod -- Class method -- by decls | TTypeSyn -- Type synonym (Non-local is not captured) | TClass -- Class (Constraint constructor) | TTypeCon -- Type (Type constructor) | TDataCon -- Data constructor - | TTypeFam -- type family - deriving (Eq, Ord) + | TTypeFamily -- type family + | TNothing -- type family + deriving (Eq, Ord, Show, Enum) -instance Show SemanticTokenType where - show SClass = "class" - show SVariable = "SVariable" - show STypeVar = "STypeVar" - show SDataCon = "SDataCon" - show SFieldName = "SFieldName" - show SNothing = "SNothing" +-- instance Show SemanticTokenType where +-- show SClass = "class" +-- show SVariable = "SVariable" +-- show STypeVar = "STypeVar" +-- show SDataCon = "SDataCon" +-- show SFieldName = "SFieldName" +-- show SNothing = "SNothing" @@ -66,30 +71,30 @@ instance Show SemanticTokenType where -- SemanticTokenModifiers.modification, -- SemanticTokenModifiers.documentation, -- SemanticTokenModifiers.defaultLibrary - -- ]; -classToken = 2 -variableToken = 8 --- enumToken = 4 -typeParameter = 6 -enumMember = 10 -property = 9 +-- -- ]; +-- classToken = 2 +-- variableToken = 8 +-- -- enumToken = 4 +-- typeParameter = 6 +-- enumMember = 10 +-- property = 9 -instance Enum SemanticTokenType where - toEnum 2 = SClass -- class - toEnum 8 = SVariable -- variable - toEnum 6 = STypeVar -- typeParameter - toEnum 10 = SDataCon -- enumMember - toEnum 9 = SFieldName -- property - toEnum 14 = SNothing -- comment - toEnum x = error $ "SemanticTokenType.toEnum: bad argument:" ++ show x +-- instance Enum SemanticTokenType where +-- toEnum 2 = SClass -- class +-- toEnum 8 = SVariable -- variable +-- toEnum 6 = STypeVar -- typeParameter +-- toEnum 10 = SDataCon -- enumMember +-- toEnum 9 = SFieldName -- property +-- toEnum 14 = SNothing -- comment +-- toEnum x = error $ "SemanticTokenType.toEnum: bad argument:" ++ show x - fromEnum SClass = classToken - fromEnum SVariable = variableToken - fromEnum STypeVar = typeParameter - fromEnum SDataCon = enumMember - fromEnum SFieldName = property - fromEnum SNothing = 14 +-- fromEnum SClass = classToken +-- fromEnum SVariable = variableToken +-- fromEnum STypeVar = typeParameter +-- fromEnum SDataCon = enumMember +-- fromEnum SFieldName = property +-- fromEnum SNothing = 14 type SemanticCollectFinal = (SemanticTokenType, LIdP GhcRn) -- { line: 2, startChar 5, length: 3, tokenType: SemanticTokenType, tokenModifiers: 3, string}, diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs index e8721bd30f..56a9bdade6 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs @@ -1,15 +1,216 @@ +{- + The query module is used to query the semantic tokens from the AST +-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE StandaloneDeriving #-} module Ide.Plugin.SemanticTokens.Query where -import Data.Map (Map) +import Control.Arrow ((&&&)) +import Data.Function (on) +import Data.Generics (everything) +import qualified Data.List as List +import Data.Map (Map) +import qualified Data.Map as Map +import Data.Maybe (fromMaybe, mapMaybe) +import Data.Ord (comparing) +import qualified Data.Set as Set +import Development.IDE (realSpan) +import Development.IDE.GHC.Compat +import qualified Extra as List +import Generics.SYB (mkQ) +import Ide.Plugin.SemanticTokens.Types --- type IdentifierMap = Map (Set ContextInfo) +----------------------- +----- identifier token map +----------------------- + +-- from declaration site to token type +type NameTokenTypeMap = Map.Map Name SemanticTokenType +type NameTokenTypeItem = (Name, ContextInfo) +-- name is unique +type IdentifierItem = (Span, SrcSpan, Name, [ContextInfo]) + +identifierItemContext :: IdentifierItem -> [ContextInfo] +identifierItemContext (span, c, x, y) = y +identifierItemName :: IdentifierItem -> Name +identifierItemName (span, c, x, y) = x + + +newtype NIdentifier = NIdentifier IdentifierItem + +instance Show NIdentifier where + show (NIdentifier (span, c, x, y)) = occNameString (nameOccName x) <> " " <> show y <> "" + <> printCompactSrcSpan c <> " " <> printCompactRealSrc span + +-- determine the precedence of context info +-- since we have multiple context info, we need to choose +-- the one with the highest level +contextInfoLevel :: ContextInfo -> Int +contextInfoLevel x = case x of + Use -> 0 + MatchBind -> 1 + IEThing _ -> 2 -- imported + TyDecl -> 3 -- type signature + ValBind bt _ _ -> 4 -- lhs of val bind overrided by + PatternBind _ _ _ -> 5 -- as in pattern + ClassTyDecl _ -> 6 -- class method + TyVarBind _ _ -> 8 + RecField _ _ -> 9 -- record field + -- data constructor, type constructor, type synonym, type family + Decl _ _ -> 10 + EvidenceVarUse -> -1 + EvidenceVarBind _ _ _ -> -1 + +instance Show (IdentifierDetails a) where + show x = show $ identInfo x + +deriving instance Show DeclType +deriving instance Show BindType +deriving instance Show RecFieldContext + +instance Show ContextInfo where + show x = case x of + Use -> "Use" + MatchBind -> "MatchBind" + IEThing _ -> "IEThing IEType" -- imported + TyDecl -> "TyDecl" + ValBind bt _ _ -> "ValBind of " <> show bt + PatternBind _ _ _ -> "PatternBind" + ClassTyDecl _ -> "ClassTyDecl" + Decl d _ -> "Decl of " <> show d + TyVarBind _ _ -> "TyVarBind" + RecField c _ -> "RecField of " <> show c + EvidenceVarBind _ _ _ -> "EvidenceVarBind" + EvidenceVarUse -> "EvidenceVarUse" + +mergeInfo :: ContextInfo -> ContextInfo -> ContextInfo +mergeInfo x y = if contextInfoLevel x > contextInfoLevel y then x else y + + +infoTokenType :: ContextInfo -> SemanticTokenType +infoTokenType x = case x of + Use -> TVariable + MatchBind -> TValBind + IEThing _ -> TVariable -- todo check names space to distinguish + TyDecl -> TVariable -- type signature + ValBind bt _ _ -> TValBind + PatternBind _ _ _ -> TPatternBind + ClassTyDecl _ -> TClassMethod + TyVarBind _ _ -> TTypeVariable + RecField _ _ -> TRecField + -- data constructor, type constructor, type synonym, type family + Decl ClassDec _ -> TClass + Decl DataDec _ -> TDataCon + Decl ConDec _ -> TTypeCon + Decl SynDec _ -> TTypeSyn + Decl FamDec _ -> TTypeFamily + -- instance dec is class method + Decl InstDec _ -> TClassMethod + Decl PatSynDec _ -> TPatternSyn + EvidenceVarUse -> TVariable + EvidenceVarBind _ _ _ -> TVariable + +-- one definition site might have multiple identifiers + + +collectInfo :: [IdentifierItem] -> [NameTokenTypeItem] +collectInfo = map (identifierItemName &&& (foldr mergeInfo Use . identifierItemContext)) + +toNameGroups :: [IdentifierItem] -> [[IdentifierItem]] +toNameGroups = List.groupBy ((==) `on` identifierItemName) . List.sortOn identifierItemName + +printCompactSrcSpan :: SrcSpan -> String +printCompactSrcSpan (RealSrcSpan x _buf) = printCompactRealSrc x +printCompactSrcSpan x = "noSrc" + +printCompactRealSrc :: RealSrcSpan -> String +printCompactRealSrc x = show (srcSpanStartLine x) <> ":" <> show (srcSpanStartCol x) <> "-" <> show (srcSpanEndCol x) + +getOriginalTextFromId :: String -> NIdentifier -> String +getOriginalTextFromId sourceCode (NIdentifier (span, c, _, _)) = fromMaybe "" $ do + tLine <- lines sourceCode List.!? (line-1) + return $ take len $ drop (startChar-1) tLine + where + line = srcSpanStartLine span + startChar = srcSpanStartCol span + len= srcSpanEndCol span - startChar -- | Recurses through the given AST to find identifiers which are -- 'InstanceValBind's. --- identifierGetter :: HieAST a -> [(Span, Identifier, IdentifierDetails a)] --- identifierGetter ast = --- let ids = [ (nodeSpan ast, c, d) | (c, d) <- Map.toList $ getNodeIds ast] --- in ids <> concatMap identifierGetter (nodeChildren ast) +identifierGetter :: HieAST a -> [IdentifierItem] +identifierGetter ast = + let ids = [ (nodeSpan ast, nameSrcSpan c, c, Set.toList $ identInfo d) | (Right c, d) <- Map.toList $ getNodeIds ast] + in ids <> concatMap identifierGetter (nodeChildren ast) + +identifierNameTokenTypeMap :: Map.Map Name ContextInfo -> NameTokenTypeMap +identifierNameTokenTypeMap xs = fmap infoTokenType xs + +toNameGroupsMap :: [NameTokenTypeItem] -> Map.Map Name ContextInfo +toNameGroupsMap = Map.fromListWith mergeInfo + +constructIdentifierMap :: HieAST a -> NameTokenTypeMap +constructIdentifierMap ast = identifierNameTokenTypeMap $ toNameGroupsMap $ collectInfo $ identifierGetter ast + + +----------------------------------------- +---- collect all names from RenamedSource +----------------------------------------- + + +toTokenType :: LIdP GhcRn -> SemanticTokenType +toTokenType locName = case occNameSpace $ occName $ unLoc locName of + x | isTcClsNameSpace x -> TClass + x | isTvNameSpace x -> TTypeVariable + x | isDataConNameSpace x -> TDataCon + x | isVarNameSpace x -> TVariable + _ -> TVariable + +nameGetter :: RenamedSource -> [SemanticCollectFinal] +nameGetter = everything (++) ([] `mkQ` nameToCollect) + +nameToCollect :: LIdP GhcRn -> [SemanticCollectFinal] +nameToCollect locName = [(toTokenType locName, locName)] + +------------------------ +-- convert to lsp format +------------------------ + +toSemanticToken :: SemanticCollectFinal -> Maybe SemanticToken +toSemanticToken ori@(tokenType, locName) = do + loc <- realSpan $ getLocA locName + let line = srcSpanStartLine loc + let startChar = srcSpanStartCol loc + let len= srcSpanEndCol loc - startChar + return + -- vscode render col start from 0 + ((line, startChar-1, len, fromEnum tokenType, 0), ori) + +-- need to take offset +toSemanticTokens :: [SemanticCollectFinal] -> [SemanticToken] +toSemanticTokens = computeOffsets . List.sortOn fst. mapMaybe toSemanticToken + where + computeOffsets :: [SemanticToken] -> [SemanticToken] + computeOffsets = ls . foldl f (1, 0, []) + f (lastLine, lastStartChar, acc) ((line, startChar, len, tokenType, tokenModifiers), locName) + = ( line, startChar, + (if lastLine == line then + (0, startChar - lastStartChar, len, tokenType, tokenModifiers) + else + (line - lastLine, startChar, len, tokenType, tokenModifiers) + , locName) :acc) + ls (_, _, acc) = List.reverse acc + +----------------------------------- +-- extract semantic tokens from ast +----------------------------------- +refineTokenType :: NameTokenTypeMap -> SemanticCollectFinal -> SemanticCollectFinal +refineTokenType m (tokenType, locName) = case Map.lookup (unLoc locName) m of + Just x -> (x, locName) + Nothing -> (TNothing, locName) +extractSemanticTokens :: forall a. HieAST a -> RenamedSource -> [SemanticToken] +extractSemanticTokens ast rs = toSemanticTokens $ map (refineTokenType iMap) $ nameGetter rs + where collections = nameGetter rs + iMap = constructIdentifierMap ast diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/T1.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/T1.hs index cfd8c09b72..8e044b3a39 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/T1.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/T1.hs @@ -1,3 +1,6 @@ +-- patter syn +{-# LANGUAGE PatternSynonyms #-} + module Main where import Data.Set (Set, insert) @@ -13,6 +16,10 @@ instance Boo Int where data Dd = Dd Int +pattern One = Foo 1 + +ggg = One + data Doo = Doo Prelude.Int type Bar1 = Int type Bar2 = Doo From ff8ea9224739a485bd3e822cb54b7bd418da48ca Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 8 Dec 2023 00:46:28 +0800 Subject: [PATCH 03/73] clean up --- hls-plugin-api/src/Ide/Types.hs | 3 +-- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 27 +++++++++---------- .../src/Ide/Plugin/SemanticTokens/Types.hs | 4 +-- .../src/Ide/Plugin/SemanticTokens/query.hs | 10 +++---- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index 4c5ce21b9a..34879739c8 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -498,8 +498,7 @@ instance PluginMethod Request Method_TextDocumentFormatting where pid = pluginId pluginDesc instance PluginMethod Request Method_TextDocumentSemanticTokensFull where - pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc - && pluginEnabledConfig plcCallHierarchyOn (configForPlugin conf pluginDesc) + pluginEnabled _ msgParams pluginDesc _ = pluginResponsible uri pluginDesc where uri = msgParams ^. L.textDocument . L.uri diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 763191e8bc..af34aeb6aa 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -53,14 +53,21 @@ import Development.IDE (IdeState, Priority (..), logWith :: (Show a, MonadIO m) => IdeState -> a -> m () logWith st = liftIO . logPriority (ideLogger st) Info . T.pack . show +bytestringString :: ByteString -> String +bytestringString = map (toEnum . fromEnum) . unpack + +computeSemanticTokens' :: forall a . String -> HieAST a -> Action (Maybe ()) +computeSemanticTokens' src hieAst = do + -- let identifiers = map NIdentifier $ identifierGetter hieAst + let identifiersGroups = (map .map) NIdentifier $ toNameGroups $ identifierGetter hieAst + + liftIO $ mapM_ (\gr -> liftIO (putStrLn $ "group size: " <> show (List.length gr)) >> mapM_ (\x -> putStrLn $ getOriginalTextFromId src x <> ":" <> show x) gr) identifiersGroups + -- liftIO $ print $ "identifiers size: " <> show ( identifiers) + pure $ Just () ----------------------- ---- the api ----------------------- -bytestringString :: ByteString -> String -bytestringString = map (toEnum . fromEnum) . unpack --- para :: MessageParams 'Method_TextDocumentSemanticTokensFull --- para = _ computeSemanticTokens :: NormalizedFilePath -> Action (Maybe SemanticTokens) computeSemanticTokens nfp = runMaybeT $ do HAR{hieAst} <- MaybeT $ use GetHieAst nfp @@ -78,17 +85,6 @@ computeSemanticTokens nfp = runMaybeT $ do _ -> MaybeT . pure $ Nothing -computeSemanticTokens' :: forall a . String -> HieAST a -> Action (Maybe ()) -computeSemanticTokens' src hieAst = do - -- let identifiers = map NIdentifier $ identifierGetter hieAst - let identifiersGroups = (map .map) NIdentifier $ toNameGroups $ identifierGetter hieAst - - liftIO $ mapM_ (\gr -> liftIO (putStrLn $ "group size: " <> show (List.length gr)) >> mapM_ (\x -> putStrLn $ getOriginalTextFromId src x <> ":" <> show x) gr) identifiersGroups - -- liftIO $ print $ "identifiers size: " <> show ( identifiers) - pure $ Just () - - - semanticTokensFull :: PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull semanticTokensFull state _ param = do let dbg = logWith state @@ -104,6 +100,7 @@ semanticTokensFull state _ param = do -- dbg $ unlines $ map show $ recoverSemanticTokens content items pure $ InL items + ----------------------- ---- convert to lsp ----------------------- diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 819f0ca2d5..1e08a92172 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -96,9 +96,9 @@ data SemanticTokenType = -- fromEnum SFieldName = property -- fromEnum SNothing = 14 -type SemanticCollectFinal = (SemanticTokenType, LIdP GhcRn) +type SemanticCollect = (SemanticTokenType, LIdP GhcRn) -- { line: 2, startChar 5, length: 3, tokenType: SemanticTokenType, tokenModifiers: 3, string}, -type SemanticToken = (SemanticTokenData, SemanticCollectFinal) +type SemanticToken = (SemanticTokenData, SemanticCollect) type SemanticTokenData = (Int, Int, Int, Int, Int) type SemanticTokenInt = Int diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs index 56a9bdade6..81a9dfeacd 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs @@ -165,17 +165,17 @@ toTokenType locName = case occNameSpace $ occName $ unLoc locName of x | isVarNameSpace x -> TVariable _ -> TVariable -nameGetter :: RenamedSource -> [SemanticCollectFinal] +nameGetter :: RenamedSource -> [SemanticCollect] nameGetter = everything (++) ([] `mkQ` nameToCollect) -nameToCollect :: LIdP GhcRn -> [SemanticCollectFinal] +nameToCollect :: LIdP GhcRn -> [SemanticCollect] nameToCollect locName = [(toTokenType locName, locName)] ------------------------ -- convert to lsp format ------------------------ -toSemanticToken :: SemanticCollectFinal -> Maybe SemanticToken +toSemanticToken :: SemanticCollect -> Maybe SemanticToken toSemanticToken ori@(tokenType, locName) = do loc <- realSpan $ getLocA locName let line = srcSpanStartLine loc @@ -186,7 +186,7 @@ toSemanticToken ori@(tokenType, locName) = do ((line, startChar-1, len, fromEnum tokenType, 0), ori) -- need to take offset -toSemanticTokens :: [SemanticCollectFinal] -> [SemanticToken] +toSemanticTokens :: [SemanticCollect] -> [SemanticToken] toSemanticTokens = computeOffsets . List.sortOn fst. mapMaybe toSemanticToken where computeOffsets :: [SemanticToken] -> [SemanticToken] @@ -204,7 +204,7 @@ toSemanticTokens = computeOffsets . List.sortOn fst. mapMaybe toSemanticToken ----------------------------------- -- extract semantic tokens from ast ----------------------------------- -refineTokenType :: NameTokenTypeMap -> SemanticCollectFinal -> SemanticCollectFinal +refineTokenType :: NameTokenTypeMap -> SemanticCollect -> SemanticCollect refineTokenType m (tokenType, locName) = case Map.lookup (unLoc locName) m of Just x -> (x, locName) Nothing -> (TNothing, locName) From 9c379e9a64ed5ded088056f94e67b5a5f50b4738 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 8 Dec 2023 02:15:44 +0800 Subject: [PATCH 04/73] map to default token types in lsp --- hls-plugin-api/src/Ide/Types.hs | 2 +- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 18 ++- .../src/Ide/Plugin/SemanticTokens/Types.hs | 141 +++++++++--------- .../src/Ide/Plugin/SemanticTokens/query.hs | 2 +- 4 files changed, 80 insertions(+), 83 deletions(-) diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index 34879739c8..c793513b18 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -697,7 +697,7 @@ instance PluginRequestMethod (Method_CustomMethod m) where combineResponses _ _ _ _ (x :| _) = x instance PluginRequestMethod Method_TextDocumentSemanticTokensFull where - combineResponses _ _ _ _ (x :| _) = x + combineResponses _ _ _ _ (x :| _) = x takeLefts :: [a |? b] -> [a] takeLefts = mapMaybe (\x -> [res | (InL res) <- Just x]) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index af34aeb6aa..64d7447bf1 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -74,12 +74,12 @@ computeSemanticTokens nfp = runMaybeT $ do source :: ByteString <- lift $ getSourceFileSource nfp let xs = Map.toList $ getAsts hieAst - liftIO $ putStrLn $ "size" <> show (List.length xs) + -- liftIO $ putStrLn $ "size" <> show (List.length xs) -- typedAst <- MaybeT $ pure $ cast hieAst case xs of (x:_) -> do - liftIO $ putStrLn $ "computeSemanticTokens': " <> show (fst x) - MaybeT $ computeSemanticTokens' (bytestringString source) $ snd x + -- liftIO $ putStrLn $ "computeSemanticTokens': " <> show (fst x) + -- MaybeT $ computeSemanticTokens' (bytestringString source) $ snd x tcM <- MaybeT $ use TypeCheck nfp pure $ toLspSemanticTokens $ extractSemanticTokens (snd x) $ tmrRenamed tcM _ -> MaybeT . pure $ Nothing @@ -97,7 +97,7 @@ semanticTokensFull state _ param = do Nothing -> pure $ InR Null Just items -> do content <- liftIO $ readFile $ fromNormalizedFilePath nfp - -- dbg $ unlines $ map show $ recoverSemanticTokens content items + liftIO $ mapM_ (putStrLn . show) $ recoverSemanticTokens content items pure $ InL items @@ -109,7 +109,7 @@ recoverSemanticTokens :: String -> SemanticTokens -> [SemanticTokenOriginal] recoverSemanticTokens sourceCode (SemanticTokens _ xs) = map (tokenOrigin sourceCode) $ recoverSemanticToken xs tokenOrigin :: String -> SemanticTokenData -> SemanticTokenOriginal -tokenOrigin sourceCode (line, startChar, len, tokenType, _) = SemanticTokenOriginal (toEnum tokenType) (Loc line startChar len) name +tokenOrigin sourceCode (line, startChar, len, tokenType, _) = SemanticTokenOriginal tokenType (Loc line startChar len) name where tLine = lines sourceCode !! (line-1) name = take len $ drop (startChar-0) tLine @@ -120,8 +120,10 @@ recoverSemanticToken xs = recoverPosition $ if length xs `mod` 5 /= 0 then panic "recoverSemanticToken: impossible" - else map toTuple $ chunksOf 5 $ map fromIntegral $ xs - where toTuple [a, b, c, d, e] = (a, b, c, d, e) + else map toTuple $ chunksOf 5 $ map fromIntegral xs + where + toTuple :: [Int] -> SemanticTokenData + toTuple [a, b, c, d, e] = (a, b, c, fromLspTokenType $ intToType d, e) toTuple _ = panic "recoverSemanticToken: impossible" -- recover to absolute position recoverPosition :: [SemanticTokenData] -> [SemanticTokenData] @@ -152,4 +154,4 @@ toLspSemanticTokens xs = SemanticTokens Nothing (concatMap toTokenInt xs) toTokenInt :: SemanticToken -> [UInt] toTokenInt ((line, startChar, len, tokenType, tokenModifiers), _) = - [fromIntegral line, fromIntegral startChar, fromIntegral len, fromIntegral tokenType, fromIntegral tokenModifiers] + [fromIntegral line, fromIntegral startChar, fromIntegral len, fromIntegral $ typeToInt $ toLspTokenType tokenType, fromIntegral tokenModifiers] diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 1e08a92172..a082ab7711 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -1,7 +1,73 @@ +{-# LANGUAGE StandaloneDeriving #-} module Ide.Plugin.SemanticTokens.Types where +import qualified Data.List as List +import qualified Data.Set as Set import Development.IDE.GHC.Compat +import Language.LSP.Protocol.Types (LspEnum (knownValues), + SemanticTokenTypes (..)) + +-- mapping from our token type to LSP default token type +toLspTokenType :: SemanticTokenType -> SemanticTokenTypes +toLspTokenType tk = case tk of + ------- + -- term + ------- + TVariable -> SemanticTokenTypes_Variable + TDataCon -> SemanticTokenTypes_EnumMember + TRecField -> SemanticTokenTypes_Property + -- most likely a function + TValBind -> SemanticTokenTypes_Function + + ------- + -- type + ------- + TClass -> SemanticTokenTypes_Class + TClassMethod -> SemanticTokenTypes_Method + TPatternBind -> SemanticTokenTypes_Parameter + TTypeVariable -> SemanticTokenTypes_TypeParameter + + -- data type is a likely a tagged sum type since we choose data constructor as enum member + -- we choose enum for data type + + -- but we are not distinguishing between data and record + -- maybe a record type - struct ? + TTypeCon -> SemanticTokenTypes_Enum + + ------------------------------------- + -- wiggle not sure if this is correct choice + ------------------------------------- + TTypeFamily -> SemanticTokenTypes_Interface + -- is a likely a data constructor result + TPatternSyn -> SemanticTokenTypes_Event + -- let it falls to other type + TTypeSyn -> SemanticTokenTypes_Type + TNothing -> SemanticTokenTypes_Namespace + +typeToInt :: SemanticTokenTypes -> Int +typeToInt x = case Set.lookupIndex x knownValues + of Just i -> i + Nothing -> 0 +intToType :: Int -> SemanticTokenTypes +intToType i = Set.elemAt i knownValues + +fromLspTokenType :: SemanticTokenTypes -> SemanticTokenType +fromLspTokenType tk = case tk of + SemanticTokenTypes_Variable -> TVariable + SemanticTokenTypes_EnumMember -> TDataCon + SemanticTokenTypes_Property -> TRecField + SemanticTokenTypes_Function -> TValBind + SemanticTokenTypes_Class -> TClass + SemanticTokenTypes_Method -> TClassMethod + SemanticTokenTypes_Parameter -> TPatternBind + SemanticTokenTypes_TypeParameter -> TTypeVariable + SemanticTokenTypes_Enum -> TTypeCon + SemanticTokenTypes_Interface -> TTypeFamily + SemanticTokenTypes_Event -> TPatternSyn + SemanticTokenTypes_Type -> TTypeSyn + SemanticTokenTypes_Namespace -> TNothing + _ -> TNothing -- data SemanticTokenType = SClass | SVariable | STypeVar | SDataCon | SNothing | SFieldName deriving (Eq, Ord) -- @@ -21,85 +87,14 @@ data SemanticTokenType = | TTypeCon -- Type (Type constructor) | TDataCon -- Data constructor | TTypeFamily -- type family - | TNothing -- type family + | TNothing -- unknown deriving (Eq, Ord, Show, Enum) --- instance Show SemanticTokenType where --- show SClass = "class" --- show SVariable = "SVariable" --- show STypeVar = "STypeVar" --- show SDataCon = "SDataCon" --- show SFieldName = "SFieldName" --- show SNothing = "SNothing" - - - - -- capability.tokenTypes = [ - -- SemanticTokenTypes.namespace, - -- SemanticTokenTypes.type, - -- SemanticTokenTypes.class, - -- SemanticTokenTypes.enum, - -- SemanticTokenTypes.interface, - -- SemanticTokenTypes.struct, - -- SemanticTokenTypes.typeParameter, - -- SemanticTokenTypes.parameter, - -- SemanticTokenTypes.variable, - -- SemanticTokenTypes.property, - -- SemanticTokenTypes.enumMember, - -- SemanticTokenTypes.event, - -- SemanticTokenTypes.function, - -- SemanticTokenTypes.method, - -- SemanticTokenTypes.macro, - -- SemanticTokenTypes.keyword, - -- SemanticTokenTypes.modifier, - -- SemanticTokenTypes.comment, - -- SemanticTokenTypes.string, - -- SemanticTokenTypes.number, - -- SemanticTokenTypes.regexp, - -- SemanticTokenTypes.operator, - -- SemanticTokenTypes.decorator - -- ]; - - -- capability.tokenModifiers = [ - -- SemanticTokenModifiers.declaration, - -- SemanticTokenModifiers.definition, - -- SemanticTokenModifiers.readonly, - -- SemanticTokenModifiers.static, - -- SemanticTokenModifiers.deprecated, - -- SemanticTokenModifiers.abstract, - -- SemanticTokenModifiers.async, - -- SemanticTokenModifiers.modification, - -- SemanticTokenModifiers.documentation, - -- SemanticTokenModifiers.defaultLibrary --- -- ]; --- classToken = 2 --- variableToken = 8 --- -- enumToken = 4 --- typeParameter = 6 --- enumMember = 10 --- property = 9 - - --- instance Enum SemanticTokenType where --- toEnum 2 = SClass -- class --- toEnum 8 = SVariable -- variable --- toEnum 6 = STypeVar -- typeParameter --- toEnum 10 = SDataCon -- enumMember --- toEnum 9 = SFieldName -- property --- toEnum 14 = SNothing -- comment --- toEnum x = error $ "SemanticTokenType.toEnum: bad argument:" ++ show x - --- fromEnum SClass = classToken --- fromEnum SVariable = variableToken --- fromEnum STypeVar = typeParameter --- fromEnum SDataCon = enumMember --- fromEnum SFieldName = property --- fromEnum SNothing = 14 type SemanticCollect = (SemanticTokenType, LIdP GhcRn) -- { line: 2, startChar 5, length: 3, tokenType: SemanticTokenType, tokenModifiers: 3, string}, type SemanticToken = (SemanticTokenData, SemanticCollect) -type SemanticTokenData = (Int, Int, Int, Int, Int) +type SemanticTokenData = (Int, Int, Int, SemanticTokenType, Int) type SemanticTokenInt = Int data SemanticTokenOriginal = SemanticTokenOriginal diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs index 81a9dfeacd..d32bc6cb51 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs @@ -183,7 +183,7 @@ toSemanticToken ori@(tokenType, locName) = do let len= srcSpanEndCol loc - startChar return -- vscode render col start from 0 - ((line, startChar-1, len, fromEnum tokenType, 0), ori) + ((line, startChar-1, len, tokenType, 0), ori) -- need to take offset toSemanticTokens :: [SemanticCollect] -> [SemanticToken] From 96c65d103460060a965d880a4bbc6f742d64cdb3 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 8 Dec 2023 03:35:12 +0800 Subject: [PATCH 05/73] use lsp makeSemanticTokens to convert to lsp SemanticTokens type --- .../src/Ide/Plugin/SemanticTokens.hs | 2 +- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 42 ++++---------- .../src/Ide/Plugin/SemanticTokens/Types.hs | 31 +++++----- .../src/Ide/Plugin/SemanticTokens/query.hs | 58 ++++++++----------- .../hls-semantic-tokens-plugin/test/Main.hs | 6 +- 5 files changed, 56 insertions(+), 83 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index 54a86858bc..c2ae315223 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -1,4 +1,4 @@ -module Ide.Plugin.SemanticTokens (descriptor, Internal.recoverSemanticTokens) where +module Ide.Plugin.SemanticTokens (descriptor) where import Development.IDE import qualified Ide.Plugin.SemanticTokens.Internal as Internal diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 64d7447bf1..af073da707 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -72,16 +72,17 @@ computeSemanticTokens :: NormalizedFilePath -> Action (Maybe SemanticTokens) computeSemanticTokens nfp = runMaybeT $ do HAR{hieAst} <- MaybeT $ use GetHieAst nfp source :: ByteString <- lift $ getSourceFileSource nfp - let xs = Map.toList $ getAsts hieAst - -- liftIO $ putStrLn $ "size" <> show (List.length xs) - -- typedAst <- MaybeT $ pure $ cast hieAst case xs of (x:_) -> do -- liftIO $ putStrLn $ "computeSemanticTokens': " <> show (fst x) -- MaybeT $ computeSemanticTokens' (bytestringString source) $ snd x tcM <- MaybeT $ use TypeCheck nfp - pure $ toLspSemanticTokens $ extractSemanticTokens (snd x) $ tmrRenamed tcM + case extractSemanticTokens' (snd x) $ tmrRenamed tcM of + Right tokens -> pure tokens + Left err -> do + liftIO $ putStrLn $ "computeSemanticTokens: " <> show err + MaybeT . pure $ Nothing _ -> MaybeT . pure $ Nothing @@ -97,36 +98,34 @@ semanticTokensFull state _ param = do Nothing -> pure $ InR Null Just items -> do content <- liftIO $ readFile $ fromNormalizedFilePath nfp - liftIO $ mapM_ (putStrLn . show) $ recoverSemanticTokens content items + liftIO $ mapM_ print $ recoverSemanticTokens content items pure $ InL items ----------------------- ----- convert to lsp +---- recover tokens ----------------------- recoverSemanticTokens :: String -> SemanticTokens -> [SemanticTokenOriginal] recoverSemanticTokens sourceCode (SemanticTokens _ xs) = map (tokenOrigin sourceCode) $ recoverSemanticToken xs -tokenOrigin :: String -> SemanticTokenData -> SemanticTokenOriginal tokenOrigin sourceCode (line, startChar, len, tokenType, _) = SemanticTokenOriginal tokenType (Loc line startChar len) name - where tLine = lines sourceCode !! (line-1) - name = take len $ drop (startChar-0) tLine - + where tLine = lines sourceCode !! (fromIntegral line-1) + name = take (fromIntegral len) $ drop (fromIntegral startChar-0) tLine -- every five elements is a token -recoverSemanticToken :: [UInt] -> [SemanticTokenData] +-- recoverSemanticToken :: [UInt] -> [SemanticTokenData] recoverSemanticToken xs = recoverPosition $ if length xs `mod` 5 /= 0 then panic "recoverSemanticToken: impossible" else map toTuple $ chunksOf 5 $ map fromIntegral xs where - toTuple :: [Int] -> SemanticTokenData + -- toTuple :: [UInt] -> SemanticTokenData toTuple [a, b, c, d, e] = (a, b, c, fromLspTokenType $ intToType d, e) toTuple _ = panic "recoverSemanticToken: impossible" -- recover to absolute position - recoverPosition :: [SemanticTokenData] -> [SemanticTokenData] + -- recoverPosition :: [SemanticTokenData] -> [SemanticTokenData] recoverPosition xs = ls $ foldl f (1, 0, []) xs where f (lastLine, lastStartChar, acc) (line, startChar, len, tokenType, tokenModifiers) @@ -138,20 +137,3 @@ recoverSemanticToken xs = ls (_, _, acc) = List.reverse acc -sourceToTokens :: RenamedSource -> SemanticTokens -sourceToTokens = toLspSemanticTokens . toSemanticTokens . nameGetter - -emptySemanticTokens :: SemanticTokens -emptySemanticTokens = SemanticTokens Nothing [] - -toLspSemanticTokens :: [SemanticToken] -> SemanticTokens -toLspSemanticTokens xs = SemanticTokens Nothing (concatMap toTokenInt xs) - - --- semanticTokenToString :: SemanticToken -> String --- semanticTokenToString ((line, startChar, len, tokenType, tokenModifiers), locName) = --- show line ++ ":" ++ show startChar ++ ":" ++ show len ++ ":" ++ show tokenType ++ ":" ++ show tokenModifiers ++ ":\n" ++ collectToString locName - -toTokenInt :: SemanticToken -> [UInt] -toTokenInt ((line, startChar, len, tokenType, tokenModifiers), _) = - [fromIntegral line, fromIntegral startChar, fromIntegral len, fromIntegral $ typeToInt $ toLspTokenType tokenType, fromIntegral tokenModifiers] diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index a082ab7711..7f80361dc1 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -5,8 +5,9 @@ module Ide.Plugin.SemanticTokens.Types where import qualified Data.List as List import qualified Data.Set as Set import Development.IDE.GHC.Compat -import Language.LSP.Protocol.Types (LspEnum (knownValues), - SemanticTokenTypes (..)) +import Language.LSP.Protocol.Types + +x = defaultSemanticTokensLegend -- mapping from our token type to LSP default token type toLspTokenType :: SemanticTokenType -> SemanticTokenTypes @@ -45,12 +46,8 @@ toLspTokenType tk = case tk of TTypeSyn -> SemanticTokenTypes_Type TNothing -> SemanticTokenTypes_Namespace -typeToInt :: SemanticTokenTypes -> Int -typeToInt x = case Set.lookupIndex x knownValues - of Just i -> i - Nothing -> 0 -intToType :: Int -> SemanticTokenTypes -intToType i = Set.elemAt i knownValues +intToType :: UInt -> SemanticTokenTypes +intToType i = Set.elemAt (fromIntegral i) knownValues fromLspTokenType :: SemanticTokenTypes -> SemanticTokenType fromLspTokenType tk = case tk of @@ -83,19 +80,23 @@ data SemanticTokenType = | TClassMethod -- Class method -- by decls | TTypeSyn -- Type synonym (Non-local is not captured) - | TClass -- Class (Constraint constructor) + | TClass -- Class (ConstraUInt constructor) | TTypeCon -- Type (Type constructor) | TDataCon -- Data constructor | TTypeFamily -- type family | TNothing -- unknown deriving (Eq, Ord, Show, Enum) +-- toAbs :: SemanticTokenData -> SemanticTokenAbsolute +-- toAbs (line, startChar, len, tokenType, tokenModifiers) = +-- SemanticTokenAbsolute line startChar len (toLspTokenType tokenType) [SemanticTokenModifiers_Declaration] type SemanticCollect = (SemanticTokenType, LIdP GhcRn) -- { line: 2, startChar 5, length: 3, tokenType: SemanticTokenType, tokenModifiers: 3, string}, -type SemanticToken = (SemanticTokenData, SemanticCollect) -type SemanticTokenData = (Int, Int, Int, SemanticTokenType, Int) -type SemanticTokenInt = Int +-- type SemanticToken = (SemanticTokenData, SemanticCollect) +-- type SemanticTokenData = (UInt, UInt, UInt, SemanticTokenType, UInt) + +type SemanticTokenUInt = UInt data SemanticTokenOriginal = SemanticTokenOriginal { tokenType :: SemanticTokenType, @@ -105,8 +106,8 @@ data SemanticTokenOriginal = SemanticTokenOriginal deriving (Show, Eq, Ord) data Loc = Loc - { line :: Int, - startChar :: Int, - len :: Int + { line :: UInt, + startChar :: UInt, + len :: UInt } deriving (Show, Eq, Ord) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs index d32bc6cb51..e0fab19137 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs @@ -1,8 +1,9 @@ {- The query module is used to query the semantic tokens from the AST -} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} module Ide.Plugin.SemanticTokens.Query where import Control.Arrow ((&&&)) import Data.Function (on) @@ -13,11 +14,13 @@ import qualified Data.Map as Map import Data.Maybe (fromMaybe, mapMaybe) import Data.Ord (comparing) import qualified Data.Set as Set +import Data.Text (Text) import Development.IDE (realSpan) import Development.IDE.GHC.Compat import qualified Extra as List import Generics.SYB (mkQ) import Ide.Plugin.SemanticTokens.Types +import Language.LSP.Protocol.Types ----------------------- @@ -171,35 +174,6 @@ nameGetter = everything (++) ([] `mkQ` nameToCollect) nameToCollect :: LIdP GhcRn -> [SemanticCollect] nameToCollect locName = [(toTokenType locName, locName)] ------------------------- --- convert to lsp format ------------------------- - -toSemanticToken :: SemanticCollect -> Maybe SemanticToken -toSemanticToken ori@(tokenType, locName) = do - loc <- realSpan $ getLocA locName - let line = srcSpanStartLine loc - let startChar = srcSpanStartCol loc - let len= srcSpanEndCol loc - startChar - return - -- vscode render col start from 0 - ((line, startChar-1, len, tokenType, 0), ori) - --- need to take offset -toSemanticTokens :: [SemanticCollect] -> [SemanticToken] -toSemanticTokens = computeOffsets . List.sortOn fst. mapMaybe toSemanticToken - where - computeOffsets :: [SemanticToken] -> [SemanticToken] - computeOffsets = ls . foldl f (1, 0, []) - f (lastLine, lastStartChar, acc) ((line, startChar, len, tokenType, tokenModifiers), locName) - = ( line, startChar, - (if lastLine == line then - (0, startChar - lastStartChar, len, tokenType, tokenModifiers) - else - (line - lastLine, startChar, len, tokenType, tokenModifiers) - , locName) :acc) - ls (_, _, acc) = List.reverse acc - ----------------------------------- -- extract semantic tokens from ast @@ -207,10 +181,26 @@ toSemanticTokens = computeOffsets . List.sortOn fst. mapMaybe toSemanticToken refineTokenType :: NameTokenTypeMap -> SemanticCollect -> SemanticCollect refineTokenType m (tokenType, locName) = case Map.lookup (unLoc locName) m of Just x -> (x, locName) - Nothing -> (TNothing, locName) + Nothing -> (tokenType, locName) -extractSemanticTokens :: forall a. HieAST a -> RenamedSource -> [SemanticToken] -extractSemanticTokens ast rs = toSemanticTokens $ map (refineTokenType iMap) $ nameGetter rs + +extractSemanticTokens' :: forall a. HieAST a -> RenamedSource -> Either Text SemanticTokens +extractSemanticTokens' ast rs = makeSemanticTokens defaultSemanticTokensLegend $ List.sort $ toAbsSemanticTokens + $ map (refineTokenType iMap) $ nameGetter rs where collections = nameGetter rs iMap = constructIdentifierMap ast + +toAbsSemanticTokens :: [SemanticCollect] -> [SemanticTokenAbsolute] +toAbsSemanticTokens = mapMaybe toAbsSemanticToken + + +toAbsSemanticToken :: SemanticCollect -> Maybe SemanticTokenAbsolute +toAbsSemanticToken ori@(tokenType, locName) = do + loc <- realSpan $ getLocA locName + let line = srcSpanStartLine loc - 1 + let startChar = srcSpanStartCol loc - 1 + let len= srcSpanEndCol loc - 1 - startChar + return $ SemanticTokenAbsolute (fromIntegral line) (fromIntegral startChar) + (fromIntegral len) (toLspTokenType tokenType) [SemanticTokenModifiers_Declaration] + diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 02e1d4e542..1be9bada04 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -97,7 +97,7 @@ main1 = do -- res <- waitForAction "SemanticTokens.semanticTokensFull" doc res <- Test.getSemanticTokens doc case res ^? _L of - Just tokens -> liftIO $ mapM_ print $ recoverSemanticTokens content tokens + -- Just tokens -> liftIO $ mapM_ print $ recoverSemanticTokens content tokens _ -> liftIO $ print "error" liftIO $ print res lenses <- getCodeLenses doc @@ -138,7 +138,7 @@ main1 = do -- liftIO $ Prelude.writeFile "out2.txt" $ showSDoc fakeDynFlags $ ppr ast -- liftIO $ Prelude.writeFile "out3.txt" $ astToString $ tm_renamed_source typecheckedModule -- liftIO $ Prelude.writeFile "out4.txt" $ Prelude.unlines (collectToString <$> nameGetter rms) - -- liftIO $ Prelude.writeFile "out5.txt" $ Prelude.unlines $ fmap semanticTokenToString $ toSemanticTokens $ nameGetter rms + -- liftIO $ Prelude.writeFile "out5.txt" $ Prelude.unlines $ fmap semanticTokenToString $ toRelativeSemanticTokens $ nameGetter rms -- liftIO $ print $ Map.size $ nodeIdentifiers $ snd $ List.head $ Map.toList mm @@ -158,7 +158,7 @@ semanticTokensTests = -- res <- waitForAction "SemanticTokens.semanticTokensFull" doc res <- Test.getSemanticTokens doc case res ^? _L of - Just tokens -> liftIO $ mapM_ print $ recoverSemanticTokens content tokens + -- Just tokens -> liftIO $ mapM_ print $ recoverSemanticTokens content tokens _ -> liftIO $ print "error" liftIO $ print res lenses <- getCodeLenses doc From a48f2a51751cf159b57b4ee6f2f4bbbce5c8694a Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 9 Dec 2023 00:11:41 +0800 Subject: [PATCH 06/73] add test and cleanup --- .../hls-semantic-tokens-plugin.cabal | 5 +- .../src/Ide/Plugin/SemanticTokens.hs | 3 +- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 56 ++++--- .../src/Ide/Plugin/SemanticTokens/Types.hs | 108 ++++++++---- .../src/Ide/Plugin/SemanticTokens/query.hs | 66 +------- .../hls-semantic-tokens-plugin/test/Main.hs | 155 +++++++----------- .../test/testdata/valBind.hs | 8 + 7 files changed, 194 insertions(+), 207 deletions(-) create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/valBind.hs diff --git a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal index e0f7a4fc3d..a0e0a1ec64 100644 --- a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal +++ b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal @@ -21,11 +21,12 @@ source-repository head library buildable: True - exposed-modules: Ide.Plugin.SemanticTokens + exposed-modules: + Ide.Plugin.SemanticTokens + Ide.Plugin.SemanticTokens.Types other-modules: Ide.Plugin.SemanticTokens.Query Ide.Plugin.SemanticTokens.Internal - Ide.Plugin.SemanticTokens.Types hs-source-dirs: src build-depends: diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index c2ae315223..2c6a5ecbb2 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -1,7 +1,8 @@ -module Ide.Plugin.SemanticTokens (descriptor) where +module Ide.Plugin.SemanticTokens (descriptor, Internal.recoverSemanticTokens) where import Development.IDE import qualified Ide.Plugin.SemanticTokens.Internal as Internal +import qualified Ide.Plugin.SemanticTokens.Types as Types import Ide.Types import Language.LSP.Protocol.Message diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index af073da707..d6ab428910 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -1,6 +1,7 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -Wno-orphans #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleInstances #-} @@ -17,7 +18,8 @@ import Data.Data (Data) import Data.Generics (everything, mkQ) import Data.List (sortBy) import qualified Data.List as List -import Data.Maybe (listToMaybe, mapMaybe) +import Data.Maybe (fromMaybe, listToMaybe, + mapMaybe) import Development.IDE (Action, GetHieAst (GetHieAst), HieAstResult (HAR, hieAst), IdeState, @@ -41,11 +43,13 @@ import Ide.Types import Control.Arrow ((&&&), (+++)) import Control.Monad.Trans.Class (lift) import Data.ByteString (ByteString, unpack) +import Data.Function (on) import Data.Generics (Typeable) -import Data.List.Extra (chunksOf) +import Data.List.Extra (chunksOf, (!?)) import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Map as Map import qualified Data.Set as Set +import Data.Text (Text) import Data.Typeable (cast) import Development.IDE (IdeState, Priority (..), ideLogger, logPriority) @@ -56,14 +60,17 @@ logWith st = liftIO . logPriority (ideLogger st) Info . T.pack . show bytestringString :: ByteString -> String bytestringString = map (toEnum . fromEnum) . unpack -computeSemanticTokens' :: forall a . String -> HieAST a -> Action (Maybe ()) -computeSemanticTokens' src hieAst = do +debugComputeSemanticTokens :: forall a . String -> HieAST a -> Action (Maybe ()) +debugComputeSemanticTokens src hieAst = do -- let identifiers = map NIdentifier $ identifierGetter hieAst let identifiersGroups = (map .map) NIdentifier $ toNameGroups $ identifierGetter hieAst liftIO $ mapM_ (\gr -> liftIO (putStrLn $ "group size: " <> show (List.length gr)) >> mapM_ (\x -> putStrLn $ getOriginalTextFromId src x <> ":" <> show x) gr) identifiersGroups -- liftIO $ print $ "identifiers size: " <> show ( identifiers) pure $ Just () + where + toNameGroups :: [IdentifierItem] -> [[IdentifierItem]] + toNameGroups = List.groupBy ((==) `on` identifierItemName) . List.sortOn identifierItemName ----------------------- ---- the api @@ -75,8 +82,6 @@ computeSemanticTokens nfp = runMaybeT $ do let xs = Map.toList $ getAsts hieAst case xs of (x:_) -> do - -- liftIO $ putStrLn $ "computeSemanticTokens': " <> show (fst x) - -- MaybeT $ computeSemanticTokens' (bytestringString source) $ snd x tcM <- MaybeT $ use TypeCheck nfp case extractSemanticTokens' (snd x) $ tmrRenamed tcM of Right tokens -> pure tokens @@ -97,8 +102,8 @@ semanticTokensFull state _ param = do case items of Nothing -> pure $ InR Null Just items -> do - content <- liftIO $ readFile $ fromNormalizedFilePath nfp - liftIO $ mapM_ print $ recoverSemanticTokens content items + -- content <- liftIO $ readFile $ fromNormalizedFilePath nfp + -- liftIO $ mapM_ (mapM_ print) $ recoverSemanticTokens content items pure $ InL items @@ -106,24 +111,29 @@ semanticTokensFull state _ param = do ---- recover tokens ----------------------- -recoverSemanticTokens :: String -> SemanticTokens -> [SemanticTokenOriginal] -recoverSemanticTokens sourceCode (SemanticTokens _ xs) = map (tokenOrigin sourceCode) $ recoverSemanticToken xs +recoverSemanticTokens :: String -> SemanticTokens -> Either Text [SemanticTokenOriginal] +recoverSemanticTokens sourceCode (SemanticTokens _ xs) = fmap (fmap $ tokenOrigin sourceCode) $ dataActualToken xs + +-- printRecoveredSemanticTokens :: [SemanticTokenOriginal] -> IO () +-- printRecoveredSemanticTokens = mapM_ print + -tokenOrigin sourceCode (line, startChar, len, tokenType, _) = SemanticTokenOriginal tokenType (Loc line startChar len) name - where tLine = lines sourceCode !! (fromIntegral line-1) - name = take (fromIntegral len) $ drop (fromIntegral startChar-0) tLine +tokenOrigin :: [Char] -> ActualToken -> SemanticTokenOriginal +tokenOrigin sourceCode (line, startChar, len, tokenType, _) = + -- convert back to count from 1 + SemanticTokenOriginal tokenType (Loc (line+1) (startChar+1) len) name + where tLine = lines sourceCode !? fromIntegral line + name = maybe "no source" (take (fromIntegral len) . drop (fromIntegral startChar)) tLine --- every five elements is a token --- recoverSemanticToken :: [UInt] -> [SemanticTokenData] -recoverSemanticToken xs = - recoverPosition $ - if length xs `mod` 5 /= 0 - then panic "recoverSemanticToken: impossible" - else map toTuple $ chunksOf 5 $ map fromIntegral xs + +dataActualToken :: [UInt] -> Either Text [ActualToken] +dataActualToken xs = maybe decodeError (Right . fmap semanticTokenAbsoluteActualToken . absolutizeTokens) + $ mapM fromTuple (chunksOf 5 $ map fromIntegral xs) where + decodeError = Left "recoverSemanticTokenRelative: wrong token data" -- toTuple :: [UInt] -> SemanticTokenData - toTuple [a, b, c, d, e] = (a, b, c, fromLspTokenType $ intToType d, e) - toTuple _ = panic "recoverSemanticToken: impossible" + fromTuple [a, b, c, d, _] = Just $ SemanticTokenRelative a b c (fromInt $ fromIntegral d) [] + fromTuple _ = Nothing -- recover to absolute position -- recoverPosition :: [SemanticTokenData] -> [SemanticTokenData] recoverPosition xs = ls $ foldl f (1, 0, []) xs @@ -135,5 +145,3 @@ recoverSemanticToken xs = (newline,newStartChar, (newline,newStartChar, len, tokenType, tokenModifiers) :acc) ls (_, _, acc) = List.reverse acc - - diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 7f80361dc1..da8dc25fc9 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -3,11 +3,17 @@ module Ide.Plugin.SemanticTokens.Types where import qualified Data.List as List +import qualified Data.List.Extra as List +import qualified Data.Map as Map +import Data.Maybe (fromMaybe) import qualified Data.Set as Set import Development.IDE.GHC.Compat +import GHC.Enum (boundedEnumFrom) import Language.LSP.Protocol.Types -x = defaultSemanticTokensLegend +-- legends :: SemanticTokensLegend +fromInt :: Int -> SemanticTokenTypes +fromInt i = Set.elemAt i knownValues -- mapping from our token type to LSP default token type toLspTokenType :: SemanticTokenType -> SemanticTokenTypes @@ -16,16 +22,15 @@ toLspTokenType tk = case tk of -- term ------- TVariable -> SemanticTokenTypes_Variable - TDataCon -> SemanticTokenTypes_EnumMember TRecField -> SemanticTokenTypes_Property -- most likely a function TValBind -> SemanticTokenTypes_Function + TClassMethod -> SemanticTokenTypes_Method ------- -- type ------- TClass -> SemanticTokenTypes_Class - TClassMethod -> SemanticTokenTypes_Method TPatternBind -> SemanticTokenTypes_Parameter TTypeVariable -> SemanticTokenTypes_TypeParameter @@ -34,7 +39,11 @@ toLspTokenType tk = case tk of -- but we are not distinguishing between data and record -- maybe a record type - struct ? + -- TTypeCon -> SemanticTokenTypes_Enum + -- TDataCon -> SemanticTokenTypes_EnumMember + TTypeCon -> SemanticTokenTypes_Enum + TDataCon -> SemanticTokenTypes_EnumMember ------------------------------------- -- wiggle not sure if this is correct choice @@ -46,52 +55,47 @@ toLspTokenType tk = case tk of TTypeSyn -> SemanticTokenTypes_Type TNothing -> SemanticTokenTypes_Namespace +lspTokenReverseMap :: Map.Map SemanticTokenTypes SemanticTokenType +lspTokenReverseMap = Map.fromList $ List.map (\x -> (toLspTokenType x, x)) $ boundedEnumFrom minBound + intToType :: UInt -> SemanticTokenTypes intToType i = Set.elemAt (fromIntegral i) knownValues fromLspTokenType :: SemanticTokenTypes -> SemanticTokenType -fromLspTokenType tk = case tk of - SemanticTokenTypes_Variable -> TVariable - SemanticTokenTypes_EnumMember -> TDataCon - SemanticTokenTypes_Property -> TRecField - SemanticTokenTypes_Function -> TValBind - SemanticTokenTypes_Class -> TClass - SemanticTokenTypes_Method -> TClassMethod - SemanticTokenTypes_Parameter -> TPatternBind - SemanticTokenTypes_TypeParameter -> TTypeVariable - SemanticTokenTypes_Enum -> TTypeCon - SemanticTokenTypes_Interface -> TTypeFamily - SemanticTokenTypes_Event -> TPatternSyn - SemanticTokenTypes_Type -> TTypeSyn - SemanticTokenTypes_Namespace -> TNothing - _ -> TNothing +fromLspTokenType tk = fromMaybe TNothing $ Map.lookup tk lspTokenReverseMap -- data SemanticTokenType = SClass | SVariable | STypeVar | SDataCon | SNothing | SFieldName deriving (Eq, Ord) --- +-- order of declarations matters data SemanticTokenType = - TVariable -- fallback to variable for all other cases + TNothing -- unknown + | TVariable -- fallback to variable for all other cases | TTypeVariable -- Type variable - -- by match | TPatternBind -- PatternBind, parameters, as values in let/in, case, lambda | TValBind -- MatchBind, valBind instance bind or regular bind | TRecField -- from match bind | TPatternSyn -- Pattern synonym - | TClassMethod -- Class method -- by decls - | TTypeSyn -- Type synonym (Non-local is not captured) | TClass -- Class (ConstraUInt constructor) + | TTypeSyn -- Type synonym (Non-local is not captured) | TTypeCon -- Type (Type constructor) | TDataCon -- Data constructor | TTypeFamily -- type family - | TNothing -- unknown - deriving (Eq, Ord, Show, Enum) + deriving (Eq, Ord, Show, Enum, Bounded) + +instance Semigroup SemanticTokenType where + -- one in higher enum is more specific + a <> b = max a b +instance Monoid SemanticTokenType where + mempty = TNothing --- toAbs :: SemanticTokenData -> SemanticTokenAbsolute --- toAbs (line, startChar, len, tokenType, tokenModifiers) = --- SemanticTokenAbsolute line startChar len (toLspTokenType tokenType) [SemanticTokenModifiers_Declaration] type SemanticCollect = (SemanticTokenType, LIdP GhcRn) +type ActualToken = (UInt, UInt, UInt, SemanticTokenType, UInt) + +semanticTokenAbsoluteActualToken :: SemanticTokenAbsolute -> ActualToken +semanticTokenAbsoluteActualToken (SemanticTokenAbsolute line startChar len tokenType tokenModifiers) = + (line, startChar, len, fromLspTokenType tokenType, 0) -- { line: 2, startChar 5, length: 3, tokenType: SemanticTokenType, tokenModifiers: 3, string}, -- type SemanticToken = (SemanticTokenData, SemanticCollect) -- type SemanticTokenData = (UInt, UInt, UInt, SemanticTokenType, UInt) @@ -111,3 +115,51 @@ data Loc = Loc len :: UInt } deriving (Show, Eq, Ord) + + +instance Show (IdentifierDetails a) where + show x = show $ identInfo x + +deriving instance Show DeclType +deriving instance Show BindType +deriving instance Show RecFieldContext + +instance Show ContextInfo where + show x = case x of + Use -> "Use" + MatchBind -> "MatchBind" + IEThing _ -> "IEThing IEType" -- imported + TyDecl -> "TyDecl" + ValBind bt _ _ -> "ValBind of " <> show bt + PatternBind _ _ _ -> "PatternBind" + ClassTyDecl _ -> "ClassTyDecl" + Decl d _ -> "Decl of " <> show d + TyVarBind _ _ -> "TyVarBind" + RecField c _ -> "RecField of " <> show c + EvidenceVarBind _ _ _ -> "EvidenceVarBind" + EvidenceVarUse -> "EvidenceVarUse" + + +printCompactSrcSpan :: SrcSpan -> String +printCompactSrcSpan (RealSrcSpan x _buf) = printCompactRealSrc x +printCompactSrcSpan x = "noSrc" + +printCompactRealSrc :: RealSrcSpan -> String +printCompactRealSrc x = show (srcSpanStartLine x) <> ":" <> show (srcSpanStartCol x) <> "-" <> show (srcSpanEndCol x) + +type IdentifierItem = (Span, SrcSpan, Name, [ContextInfo]) +newtype NIdentifier = NIdentifier IdentifierItem + +instance Show NIdentifier where + show (NIdentifier (span, c, x, y)) = occNameString (nameOccName x) <> " " <> show y <> "" + <> printCompactSrcSpan c <> " " <> printCompactRealSrc span + + +getOriginalTextFromId :: String -> NIdentifier -> String +getOriginalTextFromId sourceCode (NIdentifier (span, c, _, _)) = fromMaybe "" $ do + tLine <- lines sourceCode List.!? (line-1) + return $ take len $ drop (startChar-1) tLine + where + line = srcSpanStartLine span + startChar = srcSpanStartCol span + len= srcSpanEndCol span - startChar diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs index e0fab19137..777ea2f2b8 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs @@ -30,8 +30,6 @@ import Language.LSP.Protocol.Types -- from declaration site to token type type NameTokenTypeMap = Map.Map Name SemanticTokenType type NameTokenTypeItem = (Name, ContextInfo) --- name is unique -type IdentifierItem = (Span, SrcSpan, Name, [ContextInfo]) identifierItemContext :: IdentifierItem -> [ContextInfo] identifierItemContext (span, c, x, y) = y @@ -39,12 +37,6 @@ identifierItemName :: IdentifierItem -> Name identifierItemName (span, c, x, y) = x -newtype NIdentifier = NIdentifier IdentifierItem - -instance Show NIdentifier where - show (NIdentifier (span, c, x, y)) = occNameString (nameOccName x) <> " " <> show y <> "" - <> printCompactSrcSpan c <> " " <> printCompactRealSrc span - -- determine the precedence of context info -- since we have multiple context info, we need to choose -- the one with the highest level @@ -64,27 +56,6 @@ contextInfoLevel x = case x of EvidenceVarUse -> -1 EvidenceVarBind _ _ _ -> -1 -instance Show (IdentifierDetails a) where - show x = show $ identInfo x - -deriving instance Show DeclType -deriving instance Show BindType -deriving instance Show RecFieldContext - -instance Show ContextInfo where - show x = case x of - Use -> "Use" - MatchBind -> "MatchBind" - IEThing _ -> "IEThing IEType" -- imported - TyDecl -> "TyDecl" - ValBind bt _ _ -> "ValBind of " <> show bt - PatternBind _ _ _ -> "PatternBind" - ClassTyDecl _ -> "ClassTyDecl" - Decl d _ -> "Decl of " <> show d - TyVarBind _ _ -> "TyVarBind" - RecField c _ -> "RecField of " <> show c - EvidenceVarBind _ _ _ -> "EvidenceVarBind" - EvidenceVarUse -> "EvidenceVarUse" mergeInfo :: ContextInfo -> ContextInfo -> ContextInfo mergeInfo x y = if contextInfoLevel x > contextInfoLevel y then x else y @@ -94,7 +65,7 @@ infoTokenType :: ContextInfo -> SemanticTokenType infoTokenType x = case x of Use -> TVariable MatchBind -> TValBind - IEThing _ -> TVariable -- todo check names space to distinguish + IEThing _ -> TNothing -- todo check names space to distinguish TyDecl -> TVariable -- type signature ValBind bt _ _ -> TValBind PatternBind _ _ _ -> TPatternBind @@ -115,31 +86,13 @@ infoTokenType x = case x of -- one definition site might have multiple identifiers - +----------------------------------------- +---- construct definition map from HieAST a +----------------------------------------- collectInfo :: [IdentifierItem] -> [NameTokenTypeItem] collectInfo = map (identifierItemName &&& (foldr mergeInfo Use . identifierItemContext)) -toNameGroups :: [IdentifierItem] -> [[IdentifierItem]] -toNameGroups = List.groupBy ((==) `on` identifierItemName) . List.sortOn identifierItemName - -printCompactSrcSpan :: SrcSpan -> String -printCompactSrcSpan (RealSrcSpan x _buf) = printCompactRealSrc x -printCompactSrcSpan x = "noSrc" -printCompactRealSrc :: RealSrcSpan -> String -printCompactRealSrc x = show (srcSpanStartLine x) <> ":" <> show (srcSpanStartCol x) <> "-" <> show (srcSpanEndCol x) - -getOriginalTextFromId :: String -> NIdentifier -> String -getOriginalTextFromId sourceCode (NIdentifier (span, c, _, _)) = fromMaybe "" $ do - tLine <- lines sourceCode List.!? (line-1) - return $ take len $ drop (startChar-1) tLine - where - line = srcSpanStartLine span - startChar = srcSpanStartCol span - len= srcSpanEndCol span - startChar - --- | Recurses through the given AST to find identifiers which are --- 'InstanceValBind's. identifierGetter :: HieAST a -> [IdentifierItem] identifierGetter ast = let ids = [ (nodeSpan ast, nameSrcSpan c, c, Set.toList $ identInfo d) | (Right c, d) <- Map.toList $ getNodeIds ast] @@ -154,7 +107,6 @@ toNameGroupsMap = Map.fromListWith mergeInfo constructIdentifierMap :: HieAST a -> NameTokenTypeMap constructIdentifierMap ast = identifierNameTokenTypeMap $ toNameGroupsMap $ collectInfo $ identifierGetter ast - ----------------------------------------- ---- collect all names from RenamedSource ----------------------------------------- @@ -180,20 +132,16 @@ nameToCollect locName = [(toTokenType locName, locName)] ----------------------------------- refineTokenType :: NameTokenTypeMap -> SemanticCollect -> SemanticCollect refineTokenType m (tokenType, locName) = case Map.lookup (unLoc locName) m of - Just x -> (x, locName) + Just x -> (x <> tokenType, locName) Nothing -> (tokenType, locName) - extractSemanticTokens' :: forall a. HieAST a -> RenamedSource -> Either Text SemanticTokens -extractSemanticTokens' ast rs = makeSemanticTokens defaultSemanticTokensLegend $ List.sort $ toAbsSemanticTokens - $ map (refineTokenType iMap) $ nameGetter rs +extractSemanticTokens' ast rs = makeSemanticTokens defaultSemanticTokensLegend + $ List.sort $ mapMaybe (toAbsSemanticToken . refineTokenType iMap) (nameGetter rs) where collections = nameGetter rs iMap = constructIdentifierMap ast -toAbsSemanticTokens :: [SemanticCollect] -> [SemanticTokenAbsolute] -toAbsSemanticTokens = mapMaybe toAbsSemanticToken - toAbsSemanticToken :: SemanticCollect -> Maybe SemanticTokenAbsolute toAbsSemanticToken ori@(tokenType, locName) = do diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 1be9bada04..0b9b008d53 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -2,6 +2,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} @@ -10,7 +11,7 @@ {-# LANGUAGE TypeApplications #-} import Data.Bifunctor -import Data.ByteString as BS +import Data.ByteString as BS import Data.Default import qualified Data.Maybe import Data.Text @@ -36,36 +37,37 @@ import Development.IDE -- import Development.IDE.Test -- import Test.Hls -- import Test.Hls.Util (withCanonicalTempDir) -import Control.Arrow (Arrow ((***)), (&&&), (+++)) +import Control.Arrow (Arrow ((***)), (&&&), (+++)) import Data.Data -import Data.Functor (void) -import Data.Map as Map -import Data.String (fromString) +import Data.Functor (void) +import Data.Map as Map +import Data.String (fromString) import System.Environment.Blank import System.FilePath -- import qualified Development.IDE.Core.Shake as Shake import Ide.Types -import qualified Test.Hls (PluginTestDescriptor, - mkPluginTestDescriptor', - runSessionWithServerInTmpDir, - waitForAction) -import qualified Test.Hls.FileSystem as FS +import qualified Test.Hls (PluginTestDescriptor, + mkPluginTestDescriptor', + runSessionWithServerInTmpDir, + waitForAction) +import qualified Test.Hls.FileSystem as FS -- import Development.IDE.Plugin.Test -import Control.Lens hiding (use) -import qualified Data.List as List -import Data.Maybe (fromJust) -import qualified Data.Set as Set -import Ide.Plugin.Error (getNormalizedFilePathE) +import Control.Lens hiding (use) +import qualified Data.List as List +import Data.Maybe (fromJust) +import qualified Data.Set as Set +import Ide.Plugin.Error (getNormalizedFilePathE) import Ide.Plugin.SemanticTokens -import qualified Language.LSP.Protocol.Lens as L -import Language.LSP.Protocol.Types -import Language.LSP.Protocol.Types (SemanticTokensParams (..)) -import qualified Language.LSP.Test as Test +import Ide.Plugin.SemanticTokens.Types +import qualified Language.LSP.Protocol.Lens as L +import Language.LSP.Protocol.Types (SemanticTokens (..), + SemanticTokensParams (..)) +import qualified Language.LSP.Test as Test import Test.Hls -import Test.Hls (TextDocumentIdentifier, - getCodeLenses, openDoc, - waitForAction) -import Test.Hls.Util (withCanonicalTempDir) +import Test.Hls (TextDocumentIdentifier, + getCodeLenses, openDoc, + waitForAction) +import Test.Hls.Util (withCanonicalTempDir) getUniqueName :: (NamedThing a) => a -> Name getUniqueName = getName @@ -86,83 +88,50 @@ semanticTokensPlugin = Test.Hls.mkPluginTestDescriptor' Ide.Plugin.SemanticToken mkSemanticTokensParams :: TextDocumentIdentifier -> SemanticTokensParams mkSemanticTokensParams doc = SemanticTokensParams Nothing Nothing doc -main1 :: IO () -main1 = do - - let filePath = "./test/testdata/T1.hs" - content <- Prelude.readFile filePath - Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject "T1.hs") $ do - doc <- openDoc "T1.hs" "haskell" - res <- waitForAction "TypeCheck" doc - -- res <- waitForAction "SemanticTokens.semanticTokensFull" doc - res <- Test.getSemanticTokens doc - case res ^? _L of - -- Just tokens -> liftIO $ mapM_ print $ recoverSemanticTokens content tokens - _ -> liftIO $ print "error" - liftIO $ print res - lenses <- getCodeLenses doc - liftIO $ print lenses - - -- let filePath = "hello.hs" - -- content <- Prelude.readFile filePath - -- runGhc (Just libdir) $ do - -- dflags <- getSessionDynFlags - -- setSessionDynFlags dflags - -- target <- guessTarget filePath Nothing Nothing - -- setTargets [target] - -- load LoadAllTargets - -- -- hsc_typecheck :: Bool -- ^ Keep renamed source? - -- -- -> ModSummary -> Maybe HsParsedModule - -- -- -> Hsc (TcGblEnv, RenamedStuff) - -- modSum <- getModSummary $ mkModuleName "Main" - -- parsedModule <- parseModule modSum - -- typecheckedModule <- typecheckModule parsedModule - -- let rms = fromJust $ renamedSource typecheckedModule - -- let tcGblEnv = fst $ tm_internals_ typecheckedModule - -- -- mkHieFileWithSource :: FilePath - -- -- -> BS.ByteString - -- -- -> ModSummary - -- -- -> TcGblEnv - -- -- -> RenamedSource -> HieFile - -- -- let file = mkHieFileWithSource filePath content modSum tcGblEnv rms - -- file <- mkHieFile modSum tcGblEnv rms - -- let ast = snd $ List.head $ Map.toList $ getAsts $ hie_asts file - -- let fileContent = srcSpanFile $ nodeSpan ast - -- liftIO $ print fileContent - -- let x = getter content ast - -- -- liftIO $ mapM_ print x - -- liftIO $ Prelude.writeFile "out.txt" $ Prelude.unlines $ fmap show x - - -- let mm = getSourcedNodeInfo ast - - -- liftIO $ Prelude.writeFile "out2.txt" $ showSDoc fakeDynFlags $ ppr ast - -- liftIO $ Prelude.writeFile "out3.txt" $ astToString $ tm_renamed_source typecheckedModule - -- liftIO $ Prelude.writeFile "out4.txt" $ Prelude.unlines (collectToString <$> nameGetter rms) - -- liftIO $ Prelude.writeFile "out5.txt" $ Prelude.unlines $ fmap semanticTokenToString $ toRelativeSemanticTokens $ nameGetter rms +runSessionWithServerInDir file x = Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do + doc <- openDoc file "haskell" + res <- waitForAction "TypeCheck" doc + x doc - -- liftIO $ print $ Map.size $ nodeIdentifiers $ snd $ List.head $ Map.toList mm +runSessionWithServerInDirAndGetSemantic file x = + Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do + doc <- openDoc file "haskell" + res <- waitForAction "TypeCheck" doc + res <- Test.getSemanticTokens doc + x res doc - -- liftIO $ mapM (print . (astToString &&& (show . nameUnique) &&& (show . nameSrcSpan))) names - return () semanticTokensTests :: TestTree semanticTokensTests = testGroup "get semantic Tokens" - [ testCase "variable" $ do - let filePath = "./test/testdata/T1.hs" - content <- Prelude.readFile filePath - Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject "T1.hs") $ do - doc <- openDoc "T1.hs" "haskell" - res <- waitForAction "TypeCheck" doc - -- res <- waitForAction "SemanticTokens.semanticTokensFull" doc - res <- Test.getSemanticTokens doc - case res ^? _L of - -- Just tokens -> liftIO $ mapM_ print $ recoverSemanticTokens content tokens - _ -> liftIO $ print "error" - liftIO $ print res - lenses <- getCodeLenses doc - liftIO $ print lenses + [ + testCase "variable" $ do + -- let filePath = "./test/testdata/T1.hs" + runSessionWithServerInDirAndGetSemantic "T1.hs" $ \tokens _ -> do + case tokens ^? _L of + Just tokens -> do + liftIO $ 1 @?= 1 + _ -> error "No tokens found" + liftIO $ 1 @?= 1 + , testCase "value bind" $ do + let filePath = "./test/testdata/valBind.hs" + content <- liftIO $ Prelude.readFile filePath + let expect = [ + SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 5}, name = "hello"} + , SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 10, len = 6}, name = "String"} + , SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 5}, name = "hello"} + , SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 5}, name = "hello"} + ] + runSessionWithServerInDirAndGetSemantic "valBind.hs" $ \res doc -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ expect @?= xs) $ recoverSemanticTokens content tokens + return () + _ -> error "No tokens found" + liftIO $ 1 @?= 1 ] main :: IO () diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/valBind.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/valBind.hs new file mode 100644 index 0000000000..d70ea12fb2 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/valBind.hs @@ -0,0 +1,8 @@ +module ValBind where + + +hello :: String +hello = "Hello, Haskell!" + + + From ad6f5bf29f02b8b8ca5ae9aea651ac016c199f10 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sun, 10 Dec 2023 15:02:55 +0800 Subject: [PATCH 07/73] refine semantic type to default one in lsp --- .../src/Ide/Plugin/SemanticTokens/Types.hs | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index da8dc25fc9..faf9a70e2f 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -18,41 +18,27 @@ fromInt i = Set.elemAt i knownValues -- mapping from our token type to LSP default token type toLspTokenType :: SemanticTokenType -> SemanticTokenTypes toLspTokenType tk = case tk of - ------- - -- term - ------- TVariable -> SemanticTokenTypes_Variable TRecField -> SemanticTokenTypes_Property - -- most likely a function + -- left hand side of none pattern bind TValBind -> SemanticTokenTypes_Function + -- any pattern bind + TPatternBind -> SemanticTokenTypes_Parameter TClassMethod -> SemanticTokenTypes_Method - - ------- - -- type - ------- TClass -> SemanticTokenTypes_Class - TPatternBind -> SemanticTokenTypes_Parameter TTypeVariable -> SemanticTokenTypes_TypeParameter - - -- data type is a likely a tagged sum type since we choose data constructor as enum member - -- we choose enum for data type - - -- but we are not distinguishing between data and record - -- maybe a record type - struct ? - -- TTypeCon -> SemanticTokenTypes_Enum - -- TDataCon -> SemanticTokenTypes_EnumMember - + -- normal data type is a tagged union type look like enum type + -- and a record is a product type like struct + -- but we don't distinguish them yet TTypeCon -> SemanticTokenTypes_Enum TDataCon -> SemanticTokenTypes_EnumMember - ------------------------------------- -- wiggle not sure if this is correct choice ------------------------------------- TTypeFamily -> SemanticTokenTypes_Interface - -- is a likely a data constructor result - TPatternSyn -> SemanticTokenTypes_Event - -- let it falls to other type - TTypeSyn -> SemanticTokenTypes_Type + -- pattern syn is like a limited version of macro of constructing a data type + TPatternSyn -> SemanticTokenTypes_Macro + TTypeSyn -> SemanticTokenTypes_Macro TNothing -> SemanticTokenTypes_Namespace lspTokenReverseMap :: Map.Map SemanticTokenTypes SemanticTokenType From 0b9955d7085fae21767b70cff69e39a81fa17abc Mon Sep 17 00:00:00 2001 From: Patrick Date: Sun, 10 Dec 2023 22:43:55 +0800 Subject: [PATCH 08/73] Use tokens from hieAst instead of renamedSource and add test --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 37 ++--- .../src/Ide/Plugin/SemanticTokens/Types.hs | 59 +++++--- .../src/Ide/Plugin/SemanticTokens/query.hs | 139 ++++++++---------- .../hls-semantic-tokens-plugin/test/Main.hs | 82 ++++++++--- .../test/testdata/class.hs | 6 + .../test/testdata/patternbind.hs | 8 + .../test/testdata/record.hs | 7 + 7 files changed, 199 insertions(+), 139 deletions(-) create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/class.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/patternbind.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/record.hs diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index d6ab428910..98263325c6 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -60,12 +60,19 @@ logWith st = liftIO . logPriority (ideLogger st) Info . T.pack . show bytestringString :: ByteString -> String bytestringString = map (toEnum . fromEnum) . unpack -debugComputeSemanticTokens :: forall a . String -> HieAST a -> Action (Maybe ()) -debugComputeSemanticTokens src hieAst = do +debugComputeSemanticTokens :: forall a . String -> HieAST a -> RenamedSource -> Action (Maybe ()) +debugComputeSemanticTokens src hieAst rs = do -- let identifiers = map NIdentifier $ identifierGetter hieAst - let identifiersGroups = (map .map) NIdentifier $ toNameGroups $ identifierGetter hieAst - - liftIO $ mapM_ (\gr -> liftIO (putStrLn $ "group size: " <> show (List.length gr)) >> mapM_ (\x -> putStrLn $ getOriginalTextFromId src x <> ":" <> show x) gr) identifiersGroups + -- let ns = nameGetter rs + let identifiersGroups = (map . map) NIdentifier + $ toNameGroups + -- $ filter (\(_, name, _) -> name `List.elem` ns) + $ identifierGetter hieAst + liftIO $ mapM_ (\gr -> liftIO (putStrLn $ "group size: " <> show (List.length gr)) >> mapM_ (\x -> putStrLn $ + getOriginalTextFromId src x + <> ":" + <> show x) gr) + identifiersGroups -- liftIO $ print $ "identifiers size: " <> show ( identifiers) pure $ Just () where @@ -83,8 +90,11 @@ computeSemanticTokens nfp = runMaybeT $ do case xs of (x:_) -> do tcM <- MaybeT $ use TypeCheck nfp + MaybeT $ debugComputeSemanticTokens (bytestringString source) (snd x) $ tmrRenamed tcM case extractSemanticTokens' (snd x) $ tmrRenamed tcM of - Right tokens -> pure tokens + Right tokens -> do + liftIO $ mapM_ (\x -> mapM_ print x) $ recoverSemanticTokens (bytestringString source) tokens + pure tokens Left err -> do liftIO $ putStrLn $ "computeSemanticTokens: " <> show err MaybeT . pure $ Nothing @@ -114,9 +124,6 @@ semanticTokensFull state _ param = do recoverSemanticTokens :: String -> SemanticTokens -> Either Text [SemanticTokenOriginal] recoverSemanticTokens sourceCode (SemanticTokens _ xs) = fmap (fmap $ tokenOrigin sourceCode) $ dataActualToken xs --- printRecoveredSemanticTokens :: [SemanticTokenOriginal] -> IO () --- printRecoveredSemanticTokens = mapM_ print - tokenOrigin :: [Char] -> ActualToken -> SemanticTokenOriginal tokenOrigin sourceCode (line, startChar, len, tokenType, _) = @@ -131,17 +138,5 @@ dataActualToken xs = maybe decodeError (Right . fmap semanticTokenAbsoluteActual $ mapM fromTuple (chunksOf 5 $ map fromIntegral xs) where decodeError = Left "recoverSemanticTokenRelative: wrong token data" - -- toTuple :: [UInt] -> SemanticTokenData fromTuple [a, b, c, d, _] = Just $ SemanticTokenRelative a b c (fromInt $ fromIntegral d) [] fromTuple _ = Nothing - -- recover to absolute position - -- recoverPosition :: [SemanticTokenData] -> [SemanticTokenData] - recoverPosition xs = ls $ foldl f (1, 0, []) xs - where - f (lastLine, lastStartChar, acc) (line, startChar, len, tokenType, tokenModifiers) - = let newStartChar = if line == 0 then startChar + lastStartChar else startChar - newline = line + lastLine - in - (newline,newStartChar, - (newline,newStartChar, len, tokenType, tokenModifiers) :acc) - ls (_, _, acc) = List.reverse acc diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index faf9a70e2f..1588c53655 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -4,6 +4,7 @@ module Ide.Plugin.SemanticTokens.Types where import qualified Data.List as List import qualified Data.List.Extra as List +import qualified Data.List.NonEmpty as NE import qualified Data.Map as Map import Data.Maybe (fromMaybe) import qualified Data.Set as Set @@ -18,55 +19,50 @@ fromInt i = Set.elemAt i knownValues -- mapping from our token type to LSP default token type toLspTokenType :: SemanticTokenType -> SemanticTokenTypes toLspTokenType tk = case tk of - TVariable -> SemanticTokenTypes_Variable - TRecField -> SemanticTokenTypes_Property + -- TVariable -> SemanticTokenTypes_Variable -- left hand side of none pattern bind TValBind -> SemanticTokenTypes_Function -- any pattern bind TPatternBind -> SemanticTokenTypes_Parameter - TClassMethod -> SemanticTokenTypes_Method TClass -> SemanticTokenTypes_Class + TClassMethod -> SemanticTokenTypes_Method TTypeVariable -> SemanticTokenTypes_TypeParameter -- normal data type is a tagged union type look like enum type -- and a record is a product type like struct -- but we don't distinguish them yet TTypeCon -> SemanticTokenTypes_Enum TDataCon -> SemanticTokenTypes_EnumMember - ------------------------------------- - -- wiggle not sure if this is correct choice - ------------------------------------- - TTypeFamily -> SemanticTokenTypes_Interface + TRecField -> SemanticTokenTypes_Property -- pattern syn is like a limited version of macro of constructing a data type TPatternSyn -> SemanticTokenTypes_Macro - TTypeSyn -> SemanticTokenTypes_Macro + -- saturated type + TTypeSyn -> SemanticTokenTypes_Type + -- not sure if this is correct choice + TTypeFamily -> SemanticTokenTypes_Interface TNothing -> SemanticTokenTypes_Namespace lspTokenReverseMap :: Map.Map SemanticTokenTypes SemanticTokenType lspTokenReverseMap = Map.fromList $ List.map (\x -> (toLspTokenType x, x)) $ boundedEnumFrom minBound -intToType :: UInt -> SemanticTokenTypes -intToType i = Set.elemAt (fromIntegral i) knownValues - fromLspTokenType :: SemanticTokenTypes -> SemanticTokenType fromLspTokenType tk = fromMaybe TNothing $ Map.lookup tk lspTokenReverseMap -- data SemanticTokenType = SClass | SVariable | STypeVar | SDataCon | SNothing | SFieldName deriving (Eq, Ord) --- order of declarations matters +-- !!!! order of declarations matters data SemanticTokenType = TNothing -- unknown - | TVariable -- fallback to variable for all other cases + -- | TVariable -- fallback + | TValBind -- MatchBind, valBind instance bind or regular bind | TTypeVariable -- Type variable | TPatternBind -- PatternBind, parameters, as values in let/in, case, lambda - | TValBind -- MatchBind, valBind instance bind or regular bind - | TRecField -- from match bind - | TPatternSyn -- Pattern synonym | TClassMethod -- Class method - -- by decls + | TDataCon -- Data constructor + | TPatternSyn -- Pattern synonym + | TTypeCon -- Type (Type constructor) | TClass -- Class (ConstraUInt constructor) | TTypeSyn -- Type synonym (Non-local is not captured) - | TTypeCon -- Type (Type constructor) - | TDataCon -- Data constructor | TTypeFamily -- type family + | TRecField -- from match bind deriving (Eq, Ord, Show, Enum, Bounded) instance Semigroup SemanticTokenType where @@ -77,6 +73,10 @@ instance Monoid SemanticTokenType where type SemanticCollect = (SemanticTokenType, LIdP GhcRn) + +showCollect :: SemanticCollect -> String +showCollect (tk, L _ x) = show tk <> " " <> showSDocUnsafe (ppr x) + type ActualToken = (UInt, UInt, UInt, SemanticTokenType, UInt) semanticTokenAbsoluteActualToken :: SemanticTokenAbsolute -> ActualToken @@ -133,16 +133,29 @@ printCompactSrcSpan x = "noSrc" printCompactRealSrc :: RealSrcSpan -> String printCompactRealSrc x = show (srcSpanStartLine x) <> ":" <> show (srcSpanStartCol x) <> "-" <> show (srcSpanEndCol x) -type IdentifierItem = (Span, SrcSpan, Name, [ContextInfo]) +type IdentifierItem = (Span, Name, NE.NonEmpty ContextInfo) newtype NIdentifier = NIdentifier IdentifierItem + +toTokenType :: Name -> SemanticTokenType +toTokenType locName = case occNameSpace $ occName locName of + x | isDataConNameSpace x -> TDataCon + x | isTvNameSpace x -> TTypeVariable + x | isTcClsNameSpace x -> TTypeCon -- Type constructors and classes in the same name space for now + x | isVarNameSpace x -> TValBind + _ -> TNothing + instance Show NIdentifier where - show (NIdentifier (span, c, x, y)) = occNameString (nameOccName x) <> " " <> show y <> "" - <> printCompactSrcSpan c <> " " <> printCompactRealSrc span + show (NIdentifier (span, x, y)) = + occNameString (nameOccName x) + <> "&" <> (show $ break (==':') $ occNameString $ nameOccName x) + <> "&" <> (show $ toTokenType x) + <> " [" <> show y <> "]" + <> " nameSrc: "<> printCompactSrcSpan (nameSrcSpan x) <> " " <> printCompactRealSrc span getOriginalTextFromId :: String -> NIdentifier -> String -getOriginalTextFromId sourceCode (NIdentifier (span, c, _, _)) = fromMaybe "" $ do +getOriginalTextFromId sourceCode (NIdentifier (span, _, _)) = fromMaybe "" $ do tLine <- lines sourceCode List.!? (line-1) return $ take len $ drop (startChar-1) tLine where diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs index 777ea2f2b8..6191e1416f 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs @@ -6,15 +6,18 @@ {-# LANGUAGE StandaloneDeriving #-} module Ide.Plugin.SemanticTokens.Query where import Control.Arrow ((&&&)) +import Data.Char (isAlphaNum) import Data.Function (on) import Data.Generics (everything) import qualified Data.List as List +import qualified Data.List.NonEmpty as NE import Data.Map (Map) import qualified Data.Map as Map -import Data.Maybe (fromMaybe, mapMaybe) import Data.Ord (comparing) import qualified Data.Set as Set import Data.Text (Text) +import qualified Data.Text as Text +import qualified Data.Text.Lazy.Builder as Text import Development.IDE (realSpan) import Development.IDE.GHC.Compat import qualified Extra as List @@ -29,44 +32,23 @@ import Language.LSP.Protocol.Types -- from declaration site to token type type NameTokenTypeMap = Map.Map Name SemanticTokenType -type NameTokenTypeItem = (Name, ContextInfo) +type IdentifierNameMap = Set.Set Name +type NameTokenTypeItem = (Name, SemanticTokenType) -identifierItemContext :: IdentifierItem -> [ContextInfo] -identifierItemContext (span, c, x, y) = y identifierItemName :: IdentifierItem -> Name -identifierItemName (span, c, x, y) = x - - --- determine the precedence of context info --- since we have multiple context info, we need to choose --- the one with the highest level -contextInfoLevel :: ContextInfo -> Int -contextInfoLevel x = case x of - Use -> 0 - MatchBind -> 1 - IEThing _ -> 2 -- imported - TyDecl -> 3 -- type signature - ValBind bt _ _ -> 4 -- lhs of val bind overrided by - PatternBind _ _ _ -> 5 -- as in pattern - ClassTyDecl _ -> 6 -- class method - TyVarBind _ _ -> 8 - RecField _ _ -> 9 -- record field - -- data constructor, type constructor, type synonym, type family - Decl _ _ -> 10 - EvidenceVarUse -> -1 - EvidenceVarBind _ _ _ -> -1 +identifierItemName (span, x, y) = x -mergeInfo :: ContextInfo -> ContextInfo -> ContextInfo -mergeInfo x y = if contextInfoLevel x > contextInfoLevel y then x else y +-- mergeInfo :: ContextInfo -> ContextInfo -> ContextInfo +mergeInfo x y = x `max` y infoTokenType :: ContextInfo -> SemanticTokenType infoTokenType x = case x of - Use -> TVariable - MatchBind -> TValBind + Use -> TNothing + MatchBind -> TNothing IEThing _ -> TNothing -- todo check names space to distinguish - TyDecl -> TVariable -- type signature + TyDecl -> TNothing -- type signature ValBind bt _ _ -> TValBind PatternBind _ _ _ -> TPatternBind ClassTyDecl _ -> TClassMethod @@ -74,15 +56,15 @@ infoTokenType x = case x of RecField _ _ -> TRecField -- data constructor, type constructor, type synonym, type family Decl ClassDec _ -> TClass - Decl DataDec _ -> TDataCon - Decl ConDec _ -> TTypeCon + Decl DataDec _ -> TTypeCon + Decl ConDec _ -> TDataCon Decl SynDec _ -> TTypeSyn Decl FamDec _ -> TTypeFamily -- instance dec is class method Decl InstDec _ -> TClassMethod - Decl PatSynDec _ -> TPatternSyn - EvidenceVarUse -> TVariable - EvidenceVarBind _ _ _ -> TVariable + Decl PatSynDec _ -> TPatternSyn + EvidenceVarUse -> TNothing + EvidenceVarBind _ _ _ -> TNothing -- one definition site might have multiple identifiers @@ -90,65 +72,74 @@ infoTokenType x = case x of ---- construct definition map from HieAST a ----------------------------------------- collectInfo :: [IdentifierItem] -> [NameTokenTypeItem] -collectInfo = map (identifierItemName &&& (foldr mergeInfo Use . identifierItemContext)) +collectInfo = map (\(_, name, ty) -> (name, foldr1 (<>) $ fmap infoTokenType ty)) +isNotEvidence :: ContextInfo -> Bool +isNotEvidence EvidenceVarUse = False +isNotEvidence (EvidenceVarBind _ _ _) = False +isNotEvidence _ = True -identifierGetter :: HieAST a -> [IdentifierItem] -identifierGetter ast = - let ids = [ (nodeSpan ast, nameSrcSpan c, c, Set.toList $ identInfo d) | (Right c, d) <- Map.toList $ getNodeIds ast] - in ids <> concatMap identifierGetter (nodeChildren ast) -identifierNameTokenTypeMap :: Map.Map Name ContextInfo -> NameTokenTypeMap -identifierNameTokenTypeMap xs = fmap infoTokenType xs +getIds :: HieAST a -> [(Span, Name, NE.NonEmpty ContextInfo)] +getIds ast = [ (nodeSpan ast, c, infos) + | (Right c, d) <- Map.toList $ getNodeIds ast + , let (Just infos) = NE.nonEmpty $ Set.toList $ identInfo d + , not $ isDerivedOccName (occName c) + ] + +leafIdentifierGetter :: HieAST a -> [IdentifierItem] +leafIdentifierGetter ast = + if null $ nodeChildren ast + then getIds ast + else concatMap identifierGetter (nodeChildren ast) -toNameGroupsMap :: [NameTokenTypeItem] -> Map.Map Name ContextInfo -toNameGroupsMap = Map.fromListWith mergeInfo +identifierGetter :: HieAST a -> [IdentifierItem] +identifierGetter ast = getIds ast <> concatMap identifierGetter (nodeChildren ast) + +toNameGroupsMap :: [NameTokenTypeItem] -> NameTokenTypeMap +toNameGroupsMap = Map.mapWithKey (\name tk -> if tk == TNothing then + toTokenType name else tk) . Map.fromListWith mergeInfo -constructIdentifierMap :: HieAST a -> NameTokenTypeMap -constructIdentifierMap ast = identifierNameTokenTypeMap $ toNameGroupsMap $ collectInfo $ identifierGetter ast +constructIdentifierMap :: [IdentifierItem] -> NameTokenTypeMap +constructIdentifierMap nis = toNameGroupsMap $ collectInfo $ nis ----------------------------------------- ---- collect all names from RenamedSource ----------------------------------------- -toTokenType :: LIdP GhcRn -> SemanticTokenType -toTokenType locName = case occNameSpace $ occName $ unLoc locName of - x | isTcClsNameSpace x -> TClass - x | isTvNameSpace x -> TTypeVariable - x | isDataConNameSpace x -> TDataCon - x | isVarNameSpace x -> TVariable - _ -> TVariable +nameToCollect :: LIdP GhcRn -> [Name] +nameToCollect locName = [unLoc locName] -nameGetter :: RenamedSource -> [SemanticCollect] +nameGetter :: RenamedSource -> [Name] nameGetter = everything (++) ([] `mkQ` nameToCollect) -nameToCollect :: LIdP GhcRn -> [SemanticCollect] -nameToCollect locName = [(toTokenType locName, locName)] - ----------------------------------- -- extract semantic tokens from ast ----------------------------------- -refineTokenType :: NameTokenTypeMap -> SemanticCollect -> SemanticCollect -refineTokenType m (tokenType, locName) = case Map.lookup (unLoc locName) m of - Just x -> (x <> tokenType, locName) - Nothing -> (tokenType, locName) extractSemanticTokens' :: forall a. HieAST a -> RenamedSource -> Either Text SemanticTokens extractSemanticTokens' ast rs = makeSemanticTokens defaultSemanticTokensLegend - $ List.sort $ mapMaybe (toAbsSemanticToken . refineTokenType iMap) (nameGetter rs) - where collections = nameGetter rs - iMap = constructIdentifierMap ast - - -toAbsSemanticToken :: SemanticCollect -> Maybe SemanticTokenAbsolute -toAbsSemanticToken ori@(tokenType, locName) = do - loc <- realSpan $ getLocA locName - let line = srcSpanStartLine loc - 1 - let startChar = srcSpanStartCol loc - 1 - let len= srcSpanEndCol loc - 1 - startChar - return $ SemanticTokenAbsolute (fromIntegral line) (fromIntegral startChar) - (fromIntegral len) (toLspTokenType tokenType) [SemanticTokenModifiers_Declaration] + $ List.sort $ Map.foldMapWithKey toAbsSemanticToken $ identifierToSemanticMap iMap ids + where + -- targetNames = nameGetter rs + ids = + -- filter (\(_, name, _) -> name `List.elem` targetNames) $ + leafIdentifierGetter ast + iMap = constructIdentifierMap ids + -- refine the semantic token type + identifierToSemanticMap :: NameTokenTypeMap -> [IdentifierItem] -> Map.Map Span SemanticTokenType + identifierToSemanticMap nMap xs = Map.fromListWith (<>) $ + map (\(span, name, info) -> (span, (toTokenType name) <> Map.findWithDefault (toTokenType name) name nMap)) xs + + + -- toAbsSemanticToken :: Span -> SemanticTokenType -> SemanticTokenAbsolute + toAbsSemanticToken loc tokenType = + let line = srcSpanStartLine loc - 1 + startChar = srcSpanStartCol loc - 1 + len = srcSpanEndCol loc - 1 - startChar + in return $ SemanticTokenAbsolute (fromIntegral line) (fromIntegral startChar) + (fromIntegral len) (toLspTokenType tokenType) [SemanticTokenModifiers_Declaration] diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 0b9b008d53..121ae7199e 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -15,25 +15,8 @@ import Data.ByteString as BS import Data.Default import qualified Data.Maybe import Data.Text -import Development.IDE.GHC.Compat --- import GHC.Data.StringBuffer --- import GHC.Driver.Config.Parser --- import GHC.Hs --- import GHC.Hs.Dump --- import qualified GHC.Parser as Parser ( parseModule ) --- import GHC.Parser.Lexer --- import GHC.Platform --- import GHC.Plugins --- import GHC.Settings --- import GHC.Settings.Config --- import GHC.Generics --- import Data.Generics (everything, mkQ, extQ) --- import GHC --- import qualified GHC --- import GHC.Paths (libdir) --- import GHC.Tc.Types --- import GHC.Tc.Utils.Env (lookupGlobal) import Development.IDE +import Development.IDE.GHC.Compat -- import Development.IDE.Test -- import Test.Hls -- import Test.Hls.Util (withCanonicalTempDir) @@ -119,8 +102,7 @@ semanticTokensTests = content <- liftIO $ Prelude.readFile filePath let expect = [ SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 5}, name = "hello"} - , SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 10, len = 6}, name = "String"} - , SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 5}, name = "hello"} + , SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 10, len = 6}, name = "String"} , SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 5}, name = "hello"} ] runSessionWithServerInDirAndGetSemantic "valBind.hs" $ \res doc -> do @@ -128,7 +110,65 @@ semanticTokensTests = case res ^? _L of Just tokens -> do either (error . show) - (\ xs -> liftIO $ expect @?= xs) $ recoverSemanticTokens content tokens + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _ -> error "No tokens found" + liftIO $ 1 @?= 1 + + , testCase "record" $ do + let filePath = "./test/testdata/record.hs" + content <- liftIO $ Prelude.readFile filePath + let expect = + [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 4, startChar = 18, len = 3}, name = "foo"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 25, len = 3}, name = "Int"}] + runSessionWithServerInDirAndGetSemantic "record.hs" $ \res doc -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _ -> error "No tokens found" + liftIO $ 1 @?= 1 + , testCase "type class" $ do + let filePath = "./test/testdata/class.hs" + content <- liftIO $ Prelude.readFile filePath + let expect = + [SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 7, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 4, startChar = 11, len = 1}, name = "a"} + ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 3, len = 3}, name = "foo"} + ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 10, len = 1}, name = "a"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 15, len = 3}, name = "Int"}] + runSessionWithServerInDirAndGetSemantic "class.hs" $ \res doc -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _ -> error "No tokens found" + liftIO $ 1 @?= 1 + , testCase "pattern bind" $ do + let filePath = "./test/testdata/patternBind.hs" + content <- liftIO $ Prelude.readFile filePath + let expect = + + [SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 3, startChar = 2, len = 1}, name = "a"} + ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 3, startChar = 5, len = 1}, name = "b"} + ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 1}, name = "f"} + ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 5, startChar = 3, len = 1}, name = "x"} + ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 5, startChar = 5, len = 1}, name = "y"} + ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 5, startChar = 9, len = 1}, name = "x"} + ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 11, len = 1}, name = "+"} + ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 5, startChar = 13, len = 1}, name = "y"}] + runSessionWithServerInDirAndGetSemantic "patternBind.hs" $ \res doc -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () _ -> error "No tokens found" liftIO $ 1 @?= 1 diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/class.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/class.hs new file mode 100644 index 0000000000..f83aae34cf --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/class.hs @@ -0,0 +1,6 @@ +module Class where + + +class Foo a where + foo :: a -> Int + diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/patternbind.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/patternbind.hs new file mode 100644 index 0000000000..37d9f2e029 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/patternbind.hs @@ -0,0 +1,8 @@ +module PatternBind where + +(a, b) = (1, 2) + +f x y = x + y + + + diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/record.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/record.hs new file mode 100644 index 0000000000..9999a051f5 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/record.hs @@ -0,0 +1,7 @@ +module Record where + + +data Foo = Foo { foo :: Int } + + + From c0debe6a0dff16a1c046e194b3077a479c8b01bb Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 11 Dec 2023 04:18:28 +0800 Subject: [PATCH 09/73] use customize RefMap to get semantic type --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 124 +++++++------- .../src/Ide/Plugin/SemanticTokens/Types.hs | 45 ++++- .../src/Ide/Plugin/SemanticTokens/query.hs | 154 ++++++++---------- .../hls-semantic-tokens-plugin/test/Main.hs | 33 +++- .../test/testdata/imported.hs | 5 + .../test/testdata/patternsyn.hs | 7 + 6 files changed, 223 insertions(+), 145 deletions(-) create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/imported.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/patternsyn.hs diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 98263325c6..dd352c9221 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -11,48 +11,52 @@ module Ide.Plugin.SemanticTokens.Internal where -import Control.Lens ((^.)) -import Control.Monad.IO.Class (MonadIO, liftIO) -import Control.Monad.Trans.Maybe (MaybeT (..)) -import Data.Data (Data) -import Data.Generics (everything, mkQ) -import Data.List (sortBy) -import qualified Data.List as List -import Data.Maybe (fromMaybe, listToMaybe, - mapMaybe) -import Development.IDE (Action, GetHieAst (GetHieAst), - HieAstResult (HAR, hieAst), - IdeState, - TypeCheck (TypeCheck), - realSpan) -import Development.IDE.Core.Compile (TcModuleResult (..)) -import Development.IDE.Core.Rules (getSourceFileSource, - runAction) -import Development.IDE.Core.Shake (use) +import Control.Lens ((^.)) +import Control.Monad.IO.Class (MonadIO, liftIO) +import Control.Monad.Trans.Maybe (MaybeT (..)) +import Data.Data (Data) +import Data.Generics (everything, mkQ) +import Data.List (sortBy) +import qualified Data.List as List +import Data.Maybe (fromMaybe, listToMaybe, + mapMaybe) +import Development.IDE (Action, + GetBindings (GetBindings), + GetHieAst (GetHieAst), + HieAstResult (HAR, hieAst, refMap), + IdeState, + TypeCheck (TypeCheck), + realSpan, + useWithStaleFast) +import Development.IDE.Core.Compile (TcModuleResult (..)) +import Development.IDE.Core.Rules (getSourceFileSource, + runAction) +import Development.IDE.Core.Shake (use) import Development.IDE.GHC.Compat -import Ide.Plugin.Error (getNormalizedFilePathE) -import qualified Language.LSP.Protocol.Lens as L +import Ide.Plugin.Error (getNormalizedFilePathE) +import qualified Language.LSP.Protocol.Lens as L import Language.LSP.Protocol.Message import Language.LSP.Protocol.Types -- import Language.LSP.Protocol.Types.Common -import qualified Data.Text as T +import qualified Data.Text as T import Ide.Plugin.SemanticTokens.Query import Ide.Plugin.SemanticTokens.Types import Ide.Types -- import System.FilePath (takeExtension) -import Control.Arrow ((&&&), (+++)) -import Control.Monad.Trans.Class (lift) -import Data.ByteString (ByteString, unpack) -import Data.Function (on) -import Data.Generics (Typeable) -import Data.List.Extra (chunksOf, (!?)) -import qualified Data.List.NonEmpty as NonEmpty -import qualified Data.Map as Map -import qualified Data.Set as Set -import Data.Text (Text) -import Data.Typeable (cast) -import Development.IDE (IdeState, Priority (..), - ideLogger, logPriority) +import Control.Arrow ((&&&), (+++)) +import Control.Monad.Trans.Class (lift) +import Data.ByteString (ByteString, unpack) +import Data.Function (on) +import Data.Generics (Typeable) +import Data.List.Extra (chunksOf, (!?)) +import qualified Data.List.NonEmpty as NonEmpty +import qualified Data.Map as Map +import qualified Data.Set as Set +import Data.Text (Text) +import Data.Typeable (cast) +import Development.IDE (IdeState, Priority (..), + ideLogger, logPriority) +import Development.IDE.Core.PositionMapping (zeroMapping) logWith :: (Show a, MonadIO m) => IdeState -> a -> m () logWith st = liftIO . logPriority (ideLogger st) Info . T.pack . show @@ -60,40 +64,48 @@ logWith st = liftIO . logPriority (ideLogger st) Info . T.pack . show bytestringString :: ByteString -> String bytestringString = map (toEnum . fromEnum) . unpack -debugComputeSemanticTokens :: forall a . String -> HieAST a -> RenamedSource -> Action (Maybe ()) -debugComputeSemanticTokens src hieAst rs = do - -- let identifiers = map NIdentifier $ identifierGetter hieAst - -- let ns = nameGetter rs - let identifiersGroups = (map . map) NIdentifier - $ toNameGroups - -- $ filter (\(_, name, _) -> name `List.elem` ns) - $ identifierGetter hieAst - liftIO $ mapM_ (\gr -> liftIO (putStrLn $ "group size: " <> show (List.length gr)) >> mapM_ (\x -> putStrLn $ - getOriginalTextFromId src x - <> ":" - <> show x) gr) - identifiersGroups - -- liftIO $ print $ "identifiers size: " <> show ( identifiers) - pure $ Just () - where - toNameGroups :: [IdentifierItem] -> [[IdentifierItem]] - toNameGroups = List.groupBy ((==) `on` identifierItemName) . List.sortOn identifierItemName +-- debugComputeSemanticTokens :: forall a . String -> HieAST a -> RenamedSource -> Action (Maybe ()) +-- debugComputeSemanticTokens src hieAst rs = do +-- let identifiersGroups = (map . map) NIdentifier +-- $ toNameGroups +-- -- $ filter (\(_, name, _) -> name `List.elem` ns) +-- $ identifierGetter hieAst +-- liftIO $ mapM_ (\gr -> liftIO (putStrLn $ "group size: " <> show (List.length gr)) >> mapM_ (\x -> putStrLn $ +-- getOriginalTextFromId src x +-- <> ":" +-- <> show x) gr) +-- identifiersGroups +-- -- liftIO $ print $ "identifiers size: " <> show ( identifiers) +-- pure $ Just () +-- where +-- toNameGroups :: [IdentifierItem] -> [[IdentifierItem]] +-- toNameGroups = List.groupBy ((==) `on` identifierItemName) . List.sortOn identifierItemName +-- identifierItemName :: IdentifierItem -> Name +-- identifierItemName (span, x, y) = x ----------------------- ---- the api ----------------------- computeSemanticTokens :: NormalizedFilePath -> Action (Maybe SemanticTokens) computeSemanticTokens nfp = runMaybeT $ do - HAR{hieAst} <- MaybeT $ use GetHieAst nfp + HAR{hieAst, refMap} <- MaybeT $ use GetHieAst nfp source :: ByteString <- lift $ getSourceFileSource nfp let xs = Map.toList $ getAsts hieAst + liftIO $ print $ "hieAst size: " <> show (List.length xs) + case xs of - (x:_) -> do + ((_,ast):_) -> do tcM <- MaybeT $ use TypeCheck nfp - MaybeT $ debugComputeSemanticTokens (bytestringString source) (snd x) $ tmrRenamed tcM - case extractSemanticTokens' (snd x) $ tmrRenamed tcM of + binds <- MaybeT $ use GetBindings nfp + -- MaybeT $ debugComputeSemanticTokens (bytestringString source) (snd x) $ tmrRenamed tcM + case extractSemanticTokens ast $ tmrRenamed tcM of Right tokens -> do + let refMap = identifierGetter ast + let iMap = constructIdentifierMap refMap liftIO $ mapM_ (\x -> mapM_ print x) $ recoverSemanticTokens (bytestringString source) tokens + liftIO $ putStrLn $ showRefMap refMap + + -- liftIO $ putStrLn $ showNameTokenTypeMap iMap pure tokens Left err -> do liftIO $ putStrLn $ "computeSemanticTokens: " <> show err diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 1588c53655..c7e0fb3701 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -52,11 +52,12 @@ fromLspTokenType tk = fromMaybe TNothing $ Map.lookup tk lspTokenReverseMap data SemanticTokenType = TNothing -- unknown -- | TVariable -- fallback - | TValBind -- MatchBind, valBind instance bind or regular bind - | TTypeVariable -- Type variable + -- since many thing can be valbind. we put it as less priority + | TValBind -- valBind instance bind or regular bind | TPatternBind -- PatternBind, parameters, as values in let/in, case, lambda - | TClassMethod -- Class method | TDataCon -- Data constructor + | TTypeVariable -- Type variable + | TClassMethod -- Class method | TPatternSyn -- Pattern synonym | TTypeCon -- Type (Type constructor) | TClass -- Class (ConstraUInt constructor) @@ -133,9 +134,14 @@ printCompactSrcSpan x = "noSrc" printCompactRealSrc :: RealSrcSpan -> String printCompactRealSrc x = show (srcSpanStartLine x) <> ":" <> show (srcSpanStartCol x) <> "-" <> show (srcSpanEndCol x) + + type IdentifierItem = (Span, Name, NE.NonEmpty ContextInfo) newtype NIdentifier = NIdentifier IdentifierItem +identifierTokenType :: Identifier -> SemanticTokenType +identifierTokenType (Right x) = toTokenType x +identifierTokenType (Left x) = TNothing toTokenType :: Name -> SemanticTokenType toTokenType locName = case occNameSpace $ occName locName of @@ -162,3 +168,36 @@ getOriginalTextFromId sourceCode (NIdentifier (span, _, _)) = fromMaybe "" $ do line = srcSpanStartLine span startChar = srcSpanStartCol span len= srcSpanEndCol span - startChar + + +----------------------- +----- identifier token map +----------------------- + +-- unsafePrintDoc :: SDoc -> String + +-- from declaration site to token type +type NameTokenTypeMap = Map.Map Identifier ([Span], SemanticTokenType) + +showNameTokenTypeMap :: NameTokenTypeMap -> String +showNameTokenTypeMap m = unlines + [ + showSDocUnsafe (pprIdentifier idn) ++ ":" ++ show v + ++ "\n" ++ unlines [showSDocUnsafe (ppr span) | span <- spans] + | (idn, (spans, v)) <- Map.toList m] + + +-- type RefMap a = M.Map Identifier [(Span, IdentifierDetails a)] +showRefMap :: RefMap a -> String +showRefMap m = unlines + [ + showIdentifier idn ++ ":" + ++ "\n" ++ unlines [showSDocUnsafe (ppr span) ++ "\n" ++ showIdentifierDetails v | (span, v) <- spans] + | (idn, spans) <- Map.toList m] + +showIdentifierDetails :: IdentifierDetails a -> String +showIdentifierDetails x = show $ identInfo x + +showIdentifier :: Identifier -> String +showIdentifier (Left x) = showSDocUnsafe (ppr x) +showIdentifier (Right x) = nameStableString x diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs index 6191e1416f..a0a0ee8a2c 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs @@ -13,6 +13,8 @@ import qualified Data.List as List import qualified Data.List.NonEmpty as NE import Data.Map (Map) import qualified Data.Map as Map +import Data.Maybe (catMaybes, listToMaybe, + mapMaybe) import Data.Ord (comparing) import qualified Data.Set as Set import Data.Text (Text) @@ -20,88 +22,78 @@ import qualified Data.Text as Text import qualified Data.Text.Lazy.Builder as Text import Development.IDE (realSpan) import Development.IDE.GHC.Compat -import qualified Extra as List import Generics.SYB (mkQ) import Ide.Plugin.SemanticTokens.Types import Language.LSP.Protocol.Types ------------------------ ------ identifier token map ------------------------ - --- from declaration site to token type -type NameTokenTypeMap = Map.Map Name SemanticTokenType -type IdentifierNameMap = Set.Set Name -type NameTokenTypeItem = (Name, SemanticTokenType) - -identifierItemName :: IdentifierItem -> Name -identifierItemName (span, x, y) = x - - --- mergeInfo :: ContextInfo -> ContextInfo -> ContextInfo -mergeInfo x y = x `max` y - - -infoTokenType :: ContextInfo -> SemanticTokenType -infoTokenType x = case x of - Use -> TNothing - MatchBind -> TNothing - IEThing _ -> TNothing -- todo check names space to distinguish - TyDecl -> TNothing -- type signature - ValBind bt _ _ -> TValBind - PatternBind _ _ _ -> TPatternBind - ClassTyDecl _ -> TClassMethod - TyVarBind _ _ -> TTypeVariable - RecField _ _ -> TRecField - -- data constructor, type constructor, type synonym, type family - Decl ClassDec _ -> TClass - Decl DataDec _ -> TTypeCon - Decl ConDec _ -> TDataCon - Decl SynDec _ -> TTypeSyn - Decl FamDec _ -> TTypeFamily - -- instance dec is class method - Decl InstDec _ -> TClassMethod - Decl PatSynDec _ -> TPatternSyn - EvidenceVarUse -> TNothing - EvidenceVarBind _ _ _ -> TNothing - --- one definition site might have multiple identifiers + ----------------------------------------- ---- construct definition map from HieAST a ----------------------------------------- -collectInfo :: [IdentifierItem] -> [NameTokenTypeItem] -collectInfo = map (\(_, name, ty) -> (name, foldr1 (<>) $ fmap infoTokenType ty)) - -isNotEvidence :: ContextInfo -> Bool -isNotEvidence EvidenceVarUse = False -isNotEvidence (EvidenceVarBind _ _ _) = False -isNotEvidence _ = True - - -getIds :: HieAST a -> [(Span, Name, NE.NonEmpty ContextInfo)] -getIds ast = [ (nodeSpan ast, c, infos) - | (Right c, d) <- Map.toList $ getNodeIds ast - , let (Just infos) = NE.nonEmpty $ Set.toList $ identInfo d - , not $ isDerivedOccName (occName c) - ] - -leafIdentifierGetter :: HieAST a -> [IdentifierItem] -leafIdentifierGetter ast = - if null $ nodeChildren ast - then getIds ast - else concatMap identifierGetter (nodeChildren ast) -identifierGetter :: HieAST a -> [IdentifierItem] -identifierGetter ast = getIds ast <> concatMap identifierGetter (nodeChildren ast) -toNameGroupsMap :: [NameTokenTypeItem] -> NameTokenTypeMap -toNameGroupsMap = Map.mapWithKey (\name tk -> if tk == TNothing then - toTokenType name else tk) . Map.fromListWith mergeInfo - -constructIdentifierMap :: [IdentifierItem] -> NameTokenTypeMap -constructIdentifierMap nis = toNameGroupsMap $ collectInfo $ nis +-- do not use refMap from useAsts because it may contain ghc generated names or derived names +-- which are not useful for semantic tokens (since they are not in source code) +-- only use identifier both None derived and from source code +identifierGetter :: HieAST a -> RefMap a +identifierGetter ast = Map.unionWith (<>) (getIds ast) (Map.unionsWith (<>) $ map identifierGetter (nodeChildren ast)) + where + getIds :: HieAST a -> RefMap a + getIds ast = Map.fromList [ (Right c, [(nodeSpan ast, d)]) + | (Right c, d) <- Map.toList $ getNodeIds' ast + -- at least get one info + , let (Just infos) = NE.nonEmpty $ Set.toList $ identInfo d + -- , SourceInfo == getSourcedNodeInfo (sourcedNodeInfo ast) + , not $ isDerivedOccName (occName c) + ] + getNodeIds' :: HieAST a -> Map.Map Identifier (IdentifierDetails a) + getNodeIds' = Map.foldl' combineNodeIds Map.empty + . Map.filterWithKey (\k _ -> k == SourceInfo) + . getSourcedNodeInfo . sourcedNodeInfo + + combineNodeIds :: Map.Map Identifier (IdentifierDetails a) + -> NodeInfo a -> Map.Map Identifier (IdentifierDetails a) + -- ad `combineNodeIds` (NodeInfo SourceInfo _ bd) = bd + ad `combineNodeIds` (NodeInfo _ _ bd) = Map.unionWith (<>) ad bd + + + +constructIdentifierMap :: RefMap a -> NameTokenTypeMap +constructIdentifierMap = + Map.mapWithKey (\k vs -> + let semanticToken = foldl (<>) (identifierTokenType k) $ mapMaybe (collectToken . snd) vs + in (map fst vs, semanticToken)) + where + collectToken :: IdentifierDetails a -> Maybe SemanticTokenType + collectToken details = case NE.nonEmpty $ Set.toList $ identInfo details of + Just infos -> Just $ List.maximum $ NE.map infoTokenType infos + Nothing -> Nothing + + infoTokenType :: ContextInfo -> SemanticTokenType + infoTokenType x = case x of + Use -> TNothing + MatchBind -> TNothing + IEThing _ -> TNothing -- todo find a way to get imported name + TyDecl -> TNothing -- type signature + + ValBind bt _ _ -> TValBind + PatternBind _ _ _ -> TPatternBind + ClassTyDecl _ -> TClassMethod + TyVarBind _ _ -> TTypeVariable + RecField _ _ -> TRecField + -- data constructor, type constructor, type synonym, type family + Decl ClassDec _ -> TClass + Decl DataDec _ -> TTypeCon + Decl ConDec _ -> TDataCon + Decl SynDec _ -> TTypeSyn + Decl FamDec _ -> TTypeFamily + -- instance dec is class method + Decl InstDec _ -> TClassMethod + Decl PatSynDec _ -> TPatternSyn + EvidenceVarUse -> TNothing + EvidenceVarBind _ _ _ -> TNothing ----------------------------------------- ---- collect all names from RenamedSource @@ -120,20 +112,18 @@ nameGetter = everything (++) ([] `mkQ` nameToCollect) ----------------------------------- -extractSemanticTokens' :: forall a. HieAST a -> RenamedSource -> Either Text SemanticTokens -extractSemanticTokens' ast rs = makeSemanticTokens defaultSemanticTokensLegend - $ List.sort $ Map.foldMapWithKey toAbsSemanticToken $ identifierToSemanticMap iMap ids +extractSemanticTokens :: forall a. HieAST a -> RenamedSource -> Either Text SemanticTokens +extractSemanticTokens ast rs = makeSemanticTokens defaultSemanticTokensLegend + $ List.sort $ Map.foldMapWithKey toAbsSemanticToken $ identifierToSemanticMap iMap where - -- targetNames = nameGetter rs - ids = - -- filter (\(_, name, _) -> name `List.elem` targetNames) $ - leafIdentifierGetter ast + ids = identifierGetter ast iMap = constructIdentifierMap ids -- refine the semantic token type - identifierToSemanticMap :: NameTokenTypeMap -> [IdentifierItem] -> Map.Map Span SemanticTokenType - identifierToSemanticMap nMap xs = Map.fromListWith (<>) $ - map (\(span, name, info) -> (span, (toTokenType name) <> Map.findWithDefault (toTokenType name) name nMap)) xs - + identifierToSemanticMap :: NameTokenTypeMap -> Map.Map Span SemanticTokenType + identifierToSemanticMap nameMap = Map.fromList + [ (span, tokenType) + | (spans, tokenType) <- Map.elems nameMap + , span <- spans] -- toAbsSemanticToken :: Span -> SemanticTokenType -> SemanticTokenAbsolute toAbsSemanticToken loc tokenType = diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 121ae7199e..cb09090575 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -113,7 +113,6 @@ semanticTokensTests = (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () _ -> error "No tokens found" - liftIO $ 1 @?= 1 , testCase "record" $ do let filePath = "./test/testdata/record.hs" @@ -131,7 +130,6 @@ semanticTokensTests = (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () _ -> error "No tokens found" - liftIO $ 1 @?= 1 , testCase "type class" $ do let filePath = "./test/testdata/class.hs" content <- liftIO $ Prelude.readFile filePath @@ -149,7 +147,7 @@ semanticTokensTests = (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () _ -> error "No tokens found" - liftIO $ 1 @?= 1 + , testCase "pattern bind" $ do let filePath = "./test/testdata/patternBind.hs" content <- liftIO $ Prelude.readFile filePath @@ -171,7 +169,34 @@ semanticTokensTests = (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () _ -> error "No tokens found" - liftIO $ 1 @?= 1 + , testCase "pattern syn" $ do + let filePath = "./test/testdata/patternsyn.hs" + content <- liftIO $ Prelude.readFile filePath + let expect = [SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 5, startChar = 9, len = 3} + , name = "Foo"}] + runSessionWithServerInDirAndGetSemantic "patternsyn.hs" $ \res doc -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _ -> error "No tokens found" + , testCase "imported" $ do + let filePath = "./test/testdata/imported.hs" + content <- liftIO $ Prelude.readFile filePath + let expect = + [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 1}, name = "a"} + ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 7, len = 1}, name = "+"} + ] + runSessionWithServerInDirAndGetSemantic "imported.hs" $ \res doc -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _ -> error "No tokens found" ] main :: IO () diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/imported.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/imported.hs new file mode 100644 index 0000000000..592b13f61b --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/imported.hs @@ -0,0 +1,5 @@ +module Imported where + + +a = 1 + 1 + diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/patternsyn.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/patternsyn.hs new file mode 100644 index 0000000000..3a3e0020b2 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/patternsyn.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE PatternSynonyms #-} +module PatternSyn where + + +pattern Foo = 1 + + From 81398a36c7c60fa715600ff0a7d4a7bb646dcf7b Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 11 Dec 2023 05:24:15 +0800 Subject: [PATCH 10/73] use refMap from useAsts --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 10 +- .../src/Ide/Plugin/SemanticTokens/query.hs | 130 +++++++++--------- 2 files changed, 71 insertions(+), 69 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index dd352c9221..e25fb62f47 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -98,12 +98,14 @@ computeSemanticTokens nfp = runMaybeT $ do tcM <- MaybeT $ use TypeCheck nfp binds <- MaybeT $ use GetBindings nfp -- MaybeT $ debugComputeSemanticTokens (bytestringString source) (snd x) $ tmrRenamed tcM - case extractSemanticTokens ast $ tmrRenamed tcM of + case extractSemanticTokens refMap ast $ tmrRenamed tcM of Right tokens -> do - let refMap = identifierGetter ast - let iMap = constructIdentifierMap refMap + -- let locatedNames = List.nub $ nameGetter$ tmrRenamed tcM + -- let refMap = identifierGetter ast + -- let iMap = constructIdentifierMap refMap liftIO $ mapM_ (\x -> mapM_ print x) $ recoverSemanticTokens (bytestringString source) tokens - liftIO $ putStrLn $ showRefMap refMap + -- liftIO $ putStrLn $ showRefMap refMap + -- liftIO $ putStrLn $ showLocatedNames locatedNames -- liftIO $ putStrLn $ showNameTokenTypeMap iMap pure tokens diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs index a0a0ee8a2c..4a9b2e8ac7 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs @@ -33,15 +33,15 @@ import Language.LSP.Protocol.Types ---- construct definition map from HieAST a ----------------------------------------- - --- do not use refMap from useAsts because it may contain ghc generated names or derived names +-- do not use refMap from useAsts to get identifiers +-- because it may contain ghc generated names or derived names -- which are not useful for semantic tokens (since they are not in source code) -- only use identifier both None derived and from source code -identifierGetter :: HieAST a -> RefMap a -identifierGetter ast = Map.unionWith (<>) (getIds ast) (Map.unionsWith (<>) $ map identifierGetter (nodeChildren ast)) +identifierGetter' :: HieAST a -> [(Name, Span)] +identifierGetter' ast = getIds ast ++ concatMap identifierGetter' (nodeChildren ast) where - getIds :: HieAST a -> RefMap a - getIds ast = Map.fromList [ (Right c, [(nodeSpan ast, d)]) + getIds :: HieAST a -> [(Name, Span)] + getIds ast = [(c, nodeSpan ast) | (Right c, d) <- Map.toList $ getNodeIds' ast -- at least get one info , let (Just infos) = NE.nonEmpty $ Set.toList $ identInfo d @@ -60,51 +60,34 @@ identifierGetter ast = Map.unionWith (<>) (getIds ast) (Map.unionsWith (<>) $ ma -constructIdentifierMap :: RefMap a -> NameTokenTypeMap -constructIdentifierMap = - Map.mapWithKey (\k vs -> - let semanticToken = foldl (<>) (identifierTokenType k) $ mapMaybe (collectToken . snd) vs - in (map fst vs, semanticToken)) - where - collectToken :: IdentifierDetails a -> Maybe SemanticTokenType - collectToken details = case NE.nonEmpty $ Set.toList $ identInfo details of - Just infos -> Just $ List.maximum $ NE.map infoTokenType infos - Nothing -> Nothing - - infoTokenType :: ContextInfo -> SemanticTokenType - infoTokenType x = case x of - Use -> TNothing - MatchBind -> TNothing - IEThing _ -> TNothing -- todo find a way to get imported name - TyDecl -> TNothing -- type signature - - ValBind bt _ _ -> TValBind - PatternBind _ _ _ -> TPatternBind - ClassTyDecl _ -> TClassMethod - TyVarBind _ _ -> TTypeVariable - RecField _ _ -> TRecField - -- data constructor, type constructor, type synonym, type family - Decl ClassDec _ -> TClass - Decl DataDec _ -> TTypeCon - Decl ConDec _ -> TDataCon - Decl SynDec _ -> TTypeSyn - Decl FamDec _ -> TTypeFamily - -- instance dec is class method - Decl InstDec _ -> TClassMethod - Decl PatSynDec _ -> TPatternSyn - EvidenceVarUse -> TNothing - EvidenceVarBind _ _ _ -> TNothing - ------------------------------------------ ----- collect all names from RenamedSource ------------------------------------------ - - -nameToCollect :: LIdP GhcRn -> [Name] -nameToCollect locName = [unLoc locName] - -nameGetter :: RenamedSource -> [Name] -nameGetter = everything (++) ([] `mkQ` nameToCollect) +collectToken :: IdentifierDetails a -> Maybe SemanticTokenType +collectToken details = case NE.nonEmpty $ Set.toList $ identInfo details of + Just infos -> Just $ List.maximum $ NE.map infoTokenType infos + Nothing -> Nothing + +infoTokenType :: ContextInfo -> SemanticTokenType +infoTokenType x = case x of + Use -> TNothing + MatchBind -> TNothing + IEThing _ -> TNothing -- todo find a way to get imported name + TyDecl -> TNothing -- type signature + + ValBind bt _ _ -> TValBind + PatternBind _ _ _ -> TPatternBind + ClassTyDecl _ -> TClassMethod + TyVarBind _ _ -> TTypeVariable + RecField _ _ -> TRecField + -- data constructor, type constructor, type synonym, type family + Decl ClassDec _ -> TClass + Decl DataDec _ -> TTypeCon + Decl ConDec _ -> TDataCon + Decl SynDec _ -> TTypeSyn + Decl FamDec _ -> TTypeFamily + -- instance dec is class method + Decl InstDec _ -> TClassMethod + Decl PatSynDec _ -> TPatternSyn + EvidenceVarUse -> TNothing + EvidenceVarBind _ _ _ -> TNothing ----------------------------------- @@ -112,24 +95,41 @@ nameGetter = everything (++) ([] `mkQ` nameToCollect) ----------------------------------- -extractSemanticTokens :: forall a. HieAST a -> RenamedSource -> Either Text SemanticTokens -extractSemanticTokens ast rs = makeSemanticTokens defaultSemanticTokensLegend - $ List.sort $ Map.foldMapWithKey toAbsSemanticToken $ identifierToSemanticMap iMap +extractSemanticTokens :: forall a. RefMap a -> HieAST a -> RenamedSource -> Either Text SemanticTokens +extractSemanticTokens rm ast rs = makeSemanticTokens defaultSemanticTokensLegend + $ List.sort $ map (uncurry toAbsSemanticToken) $ mapMaybe (getSemantic (toNameSemanticMap rm)) locatedNames where - ids = identifierGetter ast - iMap = constructIdentifierMap ids - -- refine the semantic token type - identifierToSemanticMap :: NameTokenTypeMap -> Map.Map Span SemanticTokenType - identifierToSemanticMap nameMap = Map.fromList - [ (span, tokenType) - | (spans, tokenType) <- Map.elems nameMap - , span <- spans] - - -- toAbsSemanticToken :: Span -> SemanticTokenType -> SemanticTokenAbsolute + -- ids = identifierGetter ast + -- iMap = constructIdentifierMap ids + locatedNames= identifierGetter' ast + + getSemantic :: Map Name SemanticTokenType -> (Name, Span) -> Maybe (Span, SemanticTokenType) + getSemantic nameMap locName = do + let name = fst locName + let span = snd locName + let tkt = toTokenType name + let tokenType = maybe tkt (\x -> tkt <> x) $ Map.lookup name nameMap + pure (span, tokenType) + + + toNameSemanticMap :: RefMap a -> Map Name SemanticTokenType + toNameSemanticMap rm = Map.fromListWith (<>) + [ (name, tokenType) + | (Right name, details) <- Map.toList rm + , (_, detail) <- details + , let tokenType = collectToken detail + , (Just tokenType) <- [tokenType] + ] + toAbsSemanticToken loc tokenType = let line = srcSpanStartLine loc - 1 startChar = srcSpanStartCol loc - 1 len = srcSpanEndCol loc - 1 - startChar - in return $ SemanticTokenAbsolute (fromIntegral line) (fromIntegral startChar) + in SemanticTokenAbsolute (fromIntegral line) (fromIntegral startChar) (fromIntegral len) (toLspTokenType tokenType) [SemanticTokenModifiers_Declaration] + +showLocatedNames :: [LIdP GhcRn] -> String +showLocatedNames xs = unlines + [ showSDocUnsafe (ppr locName) ++ " " ++ show (getLoc locName) + | locName <- xs] From d976927d14758e73f507133d51916f3882aae560 Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 11 Dec 2023 23:38:18 +0800 Subject: [PATCH 11/73] Also compute imported names --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 53 +++++-- .../src/Ide/Plugin/SemanticTokens/Types.hs | 13 -- .../src/Ide/Plugin/SemanticTokens/query.hs | 148 ++++++++++++------ .../hls-semantic-tokens-plugin/test/Main.hs | 32 +++- .../test/testdata/moduleA.hs | 3 + .../test/testdata/moduleB.hs | 5 + 6 files changed, 172 insertions(+), 82 deletions(-) create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/moduleA.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/moduleB.hs diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index e25fb62f47..79b9de95af 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -6,6 +6,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} @@ -18,12 +19,12 @@ import Data.Data (Data) import Data.Generics (everything, mkQ) import Data.List (sortBy) import qualified Data.List as List -import Data.Maybe (fromMaybe, listToMaybe, - mapMaybe) +import Data.Maybe (catMaybes, fromMaybe, + listToMaybe, mapMaybe) import Development.IDE (Action, GetBindings (GetBindings), GetHieAst (GetHieAst), - HieAstResult (HAR, hieAst, refMap), + HieAstResult (HAR, hieAst, hieModule, refMap), IdeState, TypeCheck (TypeCheck), realSpan, @@ -31,7 +32,9 @@ import Development.IDE (Action, import Development.IDE.Core.Compile (TcModuleResult (..)) import Development.IDE.Core.Rules (getSourceFileSource, runAction) -import Development.IDE.Core.Shake (use) +import Development.IDE.Core.Shake (ShakeExtras (ShakeExtras), + getShakeExtras, use, + withHieDb) import Development.IDE.GHC.Compat import Ide.Plugin.Error (getNormalizedFilePathE) import qualified Language.LSP.Protocol.Lens as L @@ -44,6 +47,7 @@ import Ide.Plugin.SemanticTokens.Types import Ide.Types -- import System.FilePath (takeExtension) import Control.Arrow ((&&&), (+++)) +import Control.Monad (forM, forM_) import Control.Monad.Trans.Class (lift) import Data.ByteString (ByteString, unpack) import Data.Function (on) @@ -57,6 +61,8 @@ import Data.Typeable (cast) import Development.IDE (IdeState, Priority (..), ideLogger, logPriority) import Development.IDE.Core.PositionMapping (zeroMapping) +import Development.IDE.Spans.LocalBindings (getDefiningBindings, + getLocalScope) logWith :: (Show a, MonadIO m) => IdeState -> a -> m () logWith st = liftIO . logPriority (ideLogger st) Info . T.pack . show @@ -86,28 +92,32 @@ bytestringString = map (toEnum . fromEnum) . unpack ----------------------- ---- the api ----------------------- + computeSemanticTokens :: NormalizedFilePath -> Action (Maybe SemanticTokens) -computeSemanticTokens nfp = runMaybeT $ do - HAR{hieAst, refMap} <- MaybeT $ use GetHieAst nfp +computeSemanticTokens nfp = + runMaybeT $ do + -- HAR{hieAst, refMap} <- MaybeT $ use GetHieAst nfp + HAR{..} <- MaybeT $ use GetHieAst nfp + liftIO $ putStrLn $ "moduleName: " <> showSDocUnsafe (ppr hieModule) + -- (ImportMap imports, _) <- MaybeT $ useWithStaleFastMT GetImportMap file source :: ByteString <- lift $ getSourceFileSource nfp let xs = Map.toList $ getAsts hieAst liftIO $ print $ "hieAst size: " <> show (List.length xs) case xs of ((_,ast):_) -> do - tcM <- MaybeT $ use TypeCheck nfp binds <- MaybeT $ use GetBindings nfp - -- MaybeT $ debugComputeSemanticTokens (bytestringString source) (snd x) $ tmrRenamed tcM - case extractSemanticTokens refMap ast $ tmrRenamed tcM of + let importedNames = importedNameFromModule hieModule ast + ShakeExtras{withHieDb} <- MaybeT $ fmap Just getShakeExtras + nameRefPaths <- getNamesRefs withHieDb importedNames + importedModuleNameSemanticMap <- MaybeT $ fmap Just $ computeImportedSemanticTokens nameRefPaths + let originalModuleNameSemanticMap = toNameSemanticMap refMap + let combineMap = Map.unionWith (<>) originalModuleNameSemanticMap importedModuleNameSemanticMap + let names = identifierGetter ast + let moduleAbsTks = extractSemanticTokensFromNames combineMap names + case semanticTokenAbsoluteSemanticTokens moduleAbsTks of Right tokens -> do - -- let locatedNames = List.nub $ nameGetter$ tmrRenamed tcM - -- let refMap = identifierGetter ast - -- let iMap = constructIdentifierMap refMap liftIO $ mapM_ (\x -> mapM_ print x) $ recoverSemanticTokens (bytestringString source) tokens - -- liftIO $ putStrLn $ showRefMap refMap - -- liftIO $ putStrLn $ showLocatedNames locatedNames - - -- liftIO $ putStrLn $ showNameTokenTypeMap iMap pure tokens Left err -> do liftIO $ putStrLn $ "computeSemanticTokens: " <> show err @@ -154,3 +164,14 @@ dataActualToken xs = maybe decodeError (Right . fmap semanticTokenAbsoluteActual decodeError = Left "recoverSemanticTokenRelative: wrong token data" fromTuple [a, b, c, d, _] = Just $ SemanticTokenRelative a b c (fromInt $ fromIntegral d) [] fromTuple _ = Nothing + +-- span: /Users/ares/src/test/lib/SemanticTokens/Types.hs:(34,12)-(38,3) +-- type RefMap a = M.Map Identifier [(Span, IdentifierDetails a)] +computeImportedSemanticTokens :: [NormalizedFilePath] -> Action NameSemanticMap +computeImportedSemanticTokens namPathMap = do + x <- forM namPathMap $ \nfp -> runMaybeT $ do + HAR{hieAst, refMap} <- MaybeT $ use GetHieAst nfp + binds <- MaybeT $ use GetBindings nfp + return $ toNameSemanticMap refMap + let xs = catMaybes x + return $ Map.unionsWith (<>) xs diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index c7e0fb3701..6f81d0bfec 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -174,19 +174,6 @@ getOriginalTextFromId sourceCode (NIdentifier (span, _, _)) = fromMaybe "" $ do ----- identifier token map ----------------------- --- unsafePrintDoc :: SDoc -> String - --- from declaration site to token type -type NameTokenTypeMap = Map.Map Identifier ([Span], SemanticTokenType) - -showNameTokenTypeMap :: NameTokenTypeMap -> String -showNameTokenTypeMap m = unlines - [ - showSDocUnsafe (pprIdentifier idn) ++ ":" ++ show v - ++ "\n" ++ unlines [showSDocUnsafe (ppr span) | span <- spans] - | (idn, (spans, v)) <- Map.toList m] - - -- type RefMap a = M.Map Identifier [(Span, IdentifierDetails a)] showRefMap :: RefMap a -> String showRefMap m = unlines diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs index 4a9b2e8ac7..968ebd1246 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs @@ -1,30 +1,52 @@ {- The query module is used to query the semantic tokens from the AST -} +{-# LANGUAGE ExplicitNamespaces #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TypeOperators #-} module Ide.Plugin.SemanticTokens.Query where -import Control.Arrow ((&&&)) -import Data.Char (isAlphaNum) -import Data.Function (on) -import Data.Generics (everything) -import qualified Data.List as List -import qualified Data.List.NonEmpty as NE -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Maybe (catMaybes, listToMaybe, - mapMaybe) -import Data.Ord (comparing) -import qualified Data.Set as Set -import Data.Text (Text) -import qualified Data.Text as Text -import qualified Data.Text.Lazy.Builder as Text -import Development.IDE (realSpan) +import Control.Arrow ((&&&)) +import Control.Monad (forM) +import Control.Monad.IO.Class (MonadIO (liftIO)) +import Data.Char (isAlphaNum) +import Data.Function (on) +import Data.Generics (everything) +import qualified Data.List as List +import qualified Data.List.NonEmpty as NE +import Data.Map (Map) +import qualified Data.Map as Map +import Data.Maybe (catMaybes, listToMaybe, + mapMaybe) +import Data.Ord (comparing) +import qualified Data.Set as Set +import Data.Text (Text) +import qualified Data.Text as Text +import qualified Data.Text.Lazy.Builder as Text +import Development.IDE (Action, + rangeToRealSrcSpan, + realSpan) import Development.IDE.GHC.Compat -import Generics.SYB (mkQ) +import Development.IDE.Spans.AtPoint (FOIReferences) +import Development.IDE.Types.Shake (WithHieDb) +import Generics.SYB (mkQ) +import HieDb (DefRow (..), + HieDbErr (AmbiguousUnitId, NameNotFound, NameUnhelpfulSpan, NoNameAtPoint, NotIndexed), + ModuleInfo (modInfoSrcFile), + RefRow (..), Res, + findOneDef, + findReferences, + type (:.) (..)) import Ide.Plugin.SemanticTokens.Types import Language.LSP.Protocol.Types +-- import HieDb.Types (DefRow (..)) +import Data.Either (rights) +import Development.IDE (filePathToUri') +import Development.IDE.GHC.Error (positionToRealSrcLoc) +import Development.IDE.Spans.LocalBindings (Bindings) +import Development.IDE.Types.Location (toNormalizedFilePath') @@ -37,8 +59,8 @@ import Language.LSP.Protocol.Types -- because it may contain ghc generated names or derived names -- which are not useful for semantic tokens (since they are not in source code) -- only use identifier both None derived and from source code -identifierGetter' :: HieAST a -> [(Name, Span)] -identifierGetter' ast = getIds ast ++ concatMap identifierGetter' (nodeChildren ast) +identifierGetter :: HieAST a -> [(Name, Span)] +identifierGetter ast = getIds ast ++ concatMap identifierGetter (nodeChildren ast) where getIds :: HieAST a -> [(Name, Span)] getIds ast = [(c, nodeSpan ast) @@ -90,46 +112,68 @@ infoTokenType x = case x of EvidenceVarBind _ _ _ -> TNothing +type NameSemanticMap = Map Name SemanticTokenType ----------------------------------- -- extract semantic tokens from ast ----------------------------------- - -extractSemanticTokens :: forall a. RefMap a -> HieAST a -> RenamedSource -> Either Text SemanticTokens -extractSemanticTokens rm ast rs = makeSemanticTokens defaultSemanticTokensLegend - $ List.sort $ map (uncurry toAbsSemanticToken) $ mapMaybe (getSemantic (toNameSemanticMap rm)) locatedNames - where - -- ids = identifierGetter ast - -- iMap = constructIdentifierMap ids - locatedNames= identifierGetter' ast - - getSemantic :: Map Name SemanticTokenType -> (Name, Span) -> Maybe (Span, SemanticTokenType) - getSemantic nameMap locName = do - let name = fst locName - let span = snd locName - let tkt = toTokenType name - let tokenType = maybe tkt (\x -> tkt <> x) $ Map.lookup name nameMap - pure (span, tokenType) - - - toNameSemanticMap :: RefMap a -> Map Name SemanticTokenType - toNameSemanticMap rm = Map.fromListWith (<>) - [ (name, tokenType) - | (Right name, details) <- Map.toList rm - , (_, detail) <- details - , let tokenType = collectToken detail - , (Just tokenType) <- [tokenType] - ] - - toAbsSemanticToken loc tokenType = - let line = srcSpanStartLine loc - 1 - startChar = srcSpanStartCol loc - 1 - len = srcSpanEndCol loc - 1 - startChar - in SemanticTokenAbsolute (fromIntegral line) (fromIntegral startChar) - (fromIntegral len) (toLspTokenType tokenType) [SemanticTokenModifiers_Declaration] +toNameSemanticMap :: RefMap a -> NameSemanticMap +toNameSemanticMap rm = Map.fromListWith (<>) + [ (name, tokenType) + | (Right name, details) <- Map.toList rm + , (_, detail) <- details + , let tokenType = collectToken detail + , (Just tokenType) <- [tokenType] + ] +semanticTokenAbsoluteSemanticTokens :: [SemanticTokenAbsolute] -> Either Text SemanticTokens +semanticTokenAbsoluteSemanticTokens xs = makeSemanticTokens defaultSemanticTokensLegend . List.sort $ xs + +extractSemanticTokensFromNames :: NameSemanticMap -> [(Name, Span)] -> [SemanticTokenAbsolute] +extractSemanticTokensFromNames nsm = map (uncurry toAbsSemanticToken) . mapMaybe (getSemantic nsm) + +toAbsSemanticToken :: Span -> SemanticTokenType -> SemanticTokenAbsolute +toAbsSemanticToken loc tokenType = + let line = srcSpanStartLine loc - 1 + startChar = srcSpanStartCol loc - 1 + len = srcSpanEndCol loc - 1 - startChar + in SemanticTokenAbsolute (fromIntegral line) (fromIntegral startChar) + (fromIntegral len) (toLspTokenType tokenType) [SemanticTokenModifiers_Declaration] + +getSemantic :: Map Name SemanticTokenType -> (Name, Span) -> Maybe (Span, SemanticTokenType) +getSemantic nameMap locName = do + let name = fst locName + let span = snd locName + let tkt = toTokenType name + let tokenType = maybe tkt (\x -> tkt <> x) $ Map.lookup name nameMap + pure (span, tokenType) + +importedNameFromModule :: forall a. Module -> HieAST a -> Set.Set Name +importedNameFromModule mod ast = Set.fromList importedNames + where locatedNames = identifierGetter ast + importedNames = [name | (name, _) <- locatedNames, not $ nameIsLocalOrFrom mod name + , Just mod <- [nameModule_maybe name]] showLocatedNames :: [LIdP GhcRn] -> String showLocatedNames xs = unlines [ showSDocUnsafe (ppr locName) ++ " " ++ show (getLoc locName) | locName <- xs] + + +getNamesRefs + :: (MonadIO m) + => WithHieDb + -> Set.Set Name + -> m [NormalizedFilePath] +getNamesRefs withHieDb nameSet = do +-- let nameSpanMap = Map.toList $ Map.fromListWith (<>) $ (fmap . fmap) return nameSet + refs <- fmap rights $ forM (Set.toList nameSet) $ \name -> + do ans <- liftIO $ withHieDb (\hieDb -> findOneDef hieDb (nameOccName name) Nothing Nothing) + return $ ans +-- only those names that are in source code + return [path | ref <- refs, let (Just path) = rowToLoc ref] + where + rowToLoc (row:.info) = mfile + where mfile = toNormalizedFilePath <$> modInfoSrcFile info + + diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index cb09090575..6f7e434046 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -39,6 +39,7 @@ import Control.Lens hiding (use) import qualified Data.List as List import Data.Maybe (fromJust) import qualified Data.Set as Set +import Development.IDE.Plugin.Test (WaitForIdeRuleResult (..)) import Ide.Plugin.Error (getNormalizedFilePathE) import Ide.Plugin.SemanticTokens import Ide.Plugin.SemanticTokens.Types @@ -71,7 +72,8 @@ semanticTokensPlugin = Test.Hls.mkPluginTestDescriptor' Ide.Plugin.SemanticToken mkSemanticTokensParams :: TextDocumentIdentifier -> SemanticTokensParams mkSemanticTokensParams doc = SemanticTokensParams Nothing Nothing doc -runSessionWithServerInDir file x = Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do +runSessionWithServerInDir file x = + Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do doc <- openDoc file "haskell" res <- waitForAction "TypeCheck" doc x doc @@ -197,6 +199,34 @@ semanticTokensTests = (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () _ -> error "No tokens found" + + , + testCase "module import test" $ do + let filePath1 = "./test/testdata/imported.hs" + let filePath2 = "./test/testdata/imported.hs" + content1 <- liftIO $ Prelude.readFile filePath1 + content2 <- liftIO $ Prelude.readFile filePath2 + + let file1 = "moduleA.hs" + let file2 = "moduleB.hs" + let expect = [] + Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProjectMulti [file1, file2]) $ do + doc1 <- openDoc file1 "haskell" + doc2 <- openDoc file2 "haskell" + check1 <- waitForAction "TypeCheck" doc1 + check2 <- waitForAction "TypeCheck" doc2 + -- case check2 of + -- Right (WaitForIdeRuleResult x) -> liftIO $ print $ "result of checking2: " <> show x + -- Left y -> error "TypeCheck2 failed" + + -- res2 <- Test.getSemanticTokens doc2 + -- case res2 ^? _L of + -- Just tokens -> do + -- either (error . show) + -- (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content2 tokens + -- return () + -- _ -> error "No tokens found" + liftIO $ 1 @?= 1 ] main :: IO () diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/moduleA.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/moduleA.hs new file mode 100644 index 0000000000..5dfe62e22c --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/moduleA.hs @@ -0,0 +1,3 @@ +module ModuleA where + +data Game = Game Int diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/moduleB.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/moduleB.hs new file mode 100644 index 0000000000..44037eae7c --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/moduleB.hs @@ -0,0 +1,5 @@ +module ModuleA where + +import ModuleB + +go = Game 1 From 48e1311140159ca8c67c46853f3329e67c5f6722 Mon Sep 17 00:00:00 2001 From: Patrick Date: Wed, 13 Dec 2023 05:17:06 +0800 Subject: [PATCH 12/73] Also compute semantic type from TyThing --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 208 ++++++++++++++---- .../src/Ide/Plugin/SemanticTokens/query.hs | 77 ++++--- .../hls-semantic-tokens-plugin/test/Main.hs | 100 +++++++-- .../test/testdata/datatype.hs | 3 + .../test/testdata/instanceClassMethodBind.hs | 6 + .../test/testdata/instanceClassMethodUse.hs | 5 + .../test/testdata/patternMatch.hs | 6 + .../test/testdata/patternbind.hs | 1 + 8 files changed, 314 insertions(+), 92 deletions(-) create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/datatype.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodBind.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodUse.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/patternMatch.hs diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 79b9de95af..fec03523a0 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -8,7 +8,9 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE ViewPatterns #-} module Ide.Plugin.SemanticTokens.Internal where @@ -22,18 +24,23 @@ import qualified Data.List as List import Data.Maybe (catMaybes, fromMaybe, listToMaybe, mapMaybe) import Development.IDE (Action, + DocAndKindMap (DKMap), GetBindings (GetBindings), + GetDocMap (GetDocMap), GetHieAst (GetHieAst), + GetModIface (GetModIface), + GhcSessionDeps (GhcSessionDeps, GhcSessionDeps_), HieAstResult (HAR, hieAst, hieModule, refMap), IdeState, TypeCheck (TypeCheck), - realSpan, + catchSrcErrors, realSpan, useWithStaleFast) -import Development.IDE.Core.Compile (TcModuleResult (..)) +import Development.IDE.Core.Compile (TcModuleResult (..), + lookupName) import Development.IDE.Core.Rules (getSourceFileSource, runAction) -import Development.IDE.Core.Shake (ShakeExtras (ShakeExtras), - getShakeExtras, use, +import Development.IDE.Core.Shake (ShakeExtras (..), + getShakeExtras, withHieDb) import Development.IDE.GHC.Compat import Ide.Plugin.Error (getNormalizedFilePathE) @@ -50,79 +57,184 @@ import Control.Arrow ((&&&), (+++)) import Control.Monad (forM, forM_) import Control.Monad.Trans.Class (lift) import Data.ByteString (ByteString, unpack) +import Data.Either (fromRight, rights) +import Data.Either.Extra (lefts) import Data.Function (on) import Data.Generics (Typeable) +import qualified Data.HashSet as HashSet import Data.List.Extra (chunksOf, (!?)) import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Map as Map import qualified Data.Set as Set import Data.Text (Text) +import Data.Traversable (for) import Data.Typeable (cast) -import Development.IDE (IdeState, Priority (..), - ideLogger, logPriority) +import Development.IDE (IdeAction, IdeState, + Priority (..), ideLogger, + logPriority, use, uses) +import Development.IDE.Core.PluginUtils (useWithStaleFastMT, + useWithStaleMT, usesMT) import Development.IDE.Core.PositionMapping (zeroMapping) +import Development.IDE.Spans.Documentation (mkDocMap) import Development.IDE.Spans.LocalBindings (getDefiningBindings, getLocalScope) +import Development.IDE.Types.Exports (ExportsMap (..), + createExportsMapHieDb) +import Development.IDE.Types.HscEnvEq (hscEnv) +import GHC.Conc (readTVar) -logWith :: (Show a, MonadIO m) => IdeState -> a -> m () -logWith st = liftIO . logPriority (ideLogger st) Info . T.pack . show +-- logWith :: (MonadIO m) => IdeState -> String -> m () +-- logWith st = liftIO . logPriority (ideLogger st) Info . T.pack . show + +logWith :: (MonadIO m) => IdeState -> String -> m () +logWith st = liftIO . print bytestringString :: ByteString -> String bytestringString = map (toEnum . fromEnum) . unpack --- debugComputeSemanticTokens :: forall a . String -> HieAST a -> RenamedSource -> Action (Maybe ()) --- debugComputeSemanticTokens src hieAst rs = do --- let identifiersGroups = (map . map) NIdentifier --- $ toNameGroups --- -- $ filter (\(_, name, _) -> name `List.elem` ns) --- $ identifierGetter hieAst --- liftIO $ mapM_ (\gr -> liftIO (putStrLn $ "group size: " <> show (List.length gr)) >> mapM_ (\x -> putStrLn $ --- getOriginalTextFromId src x --- <> ":" --- <> show x) gr) --- identifiersGroups --- -- liftIO $ print $ "identifiers size: " <> show ( identifiers) --- pure $ Just () --- where --- toNameGroups :: [IdentifierItem] -> [[IdentifierItem]] --- toNameGroups = List.groupBy ((==) `on` identifierItemName) . List.sortOn identifierItemName --- identifierItemName :: IdentifierItem -> Name --- identifierItemName (span, x, y) = x +-- data TyThing +-- = AnId Id +-- | AConLike ConLike +-- | ATyCon TyCon -- TyCons and classes; see Note [ATyCon for classes] +-- | ACoAxiom (CoAxiom Branched) +-- a :: IdDetails +-- a = undefined + +-- | Identifier Details +-- +-- The 'IdDetails' of an 'Id' give stable, and necessary, +-- information about the Id. +-- data IdDetails +-- = VanillaId + +-- -- | The 'Id' for a record selector +-- | RecSelId +-- { sel_tycon :: RecSelParent +-- , sel_naughty :: Bool -- True <=> a "naughty" selector which can't actually exist, for example @x@ in: +-- -- data T = forall a. MkT { x :: a } +-- } -- See Note [Naughty record selectors] in GHC.Tc.TyCl + +-- | DataConWorkId DataCon -- ^ The 'Id' is for a data constructor /worker/ +-- | DataConWrapId DataCon -- ^ The 'Id' is for a data constructor /wrapper/ + +-- -- [the only reasons we need to know is so that +-- -- a) to support isImplicitId +-- -- b) when desugaring a RecordCon we can get +-- -- from the Id back to the data con] +-- | ClassOpId Class -- ^ The 'Id' is a superclass selector, +-- -- or class operation of a class + +-- | PrimOpId PrimOp Bool -- ^ The 'Id' is for a primitive operator +-- -- True <=> is representation-polymorphic, +-- -- and hence has no binding +-- -- This lev-poly flag is used only in GHC.Types.Id.hasNoBinding + +-- | FCallId ForeignCall -- ^ The 'Id' is for a foreign call. +-- -- Type will be simple: no type families, newtypes, etc + +-- | TickBoxOpId TickBoxOp -- ^ The 'Id' is for a HPC tick box (both traditional and binary) + +-- | DFunId Bool -- ^ A dictionary function. +-- -- Bool = True <=> the class has only one method, so may be +-- -- implemented with a newtype, so it might be bad +-- -- to be strict on this dictionary + +-- | CoVarId -- ^ A coercion variable +-- -- This only covers /un-lifted/ coercions, of type +-- -- (t1 ~# t2) or (t1 ~R# t2), not their lifted variants +-- | JoinId JoinArity (Maybe [CbvMark]) +-- -- ^ An 'Id' for a join point taking n arguments +-- -- Note [Join points] in "GHC.Core" +-- -- Can also work as a WorkerLikeId if given `CbvMark`s. +-- -- See Note [CBV Function Ids] +-- -- The [CbvMark] is always empty (and ignored) until after Tidy. +-- | WorkerLikeId [CbvMark] +-- -- ^ An 'Id' for a worker like function, which might expect some arguments to be +-- -- passed both evaluated and tagged. +-- -- Worker like functions are create by W/W and SpecConstr and we can expect that they +-- -- aren't used unapplied. +-- -- See Note [CBV Function Ids] +-- -- See Note [Tag Inference] +-- -- The [CbvMark] is always empty (and ignored) until after Tidy for ids from the current +-- -- module. + +tyThingSemantic :: TyThing -> SemanticTokenType +tyThingSemantic ty = case ty of + AnId vid + | isTyVar vid -> TTypeVariable + | isRecordSelector vid -> TRecField + | isClassOpId vid -> TClassMethod + -- | isLocalId vid -> TPatternBind + -- | isDFunId vid -> TClassMethod + | otherwise -> TValBind + AConLike con -> case con of + RealDataCon _ -> TDataCon + PatSynCon _ -> TPatternSyn + ATyCon tyCon + | isDataTyCon tyCon -> TTypeCon + | isPrimTyCon tyCon -> TTypeCon + | isClassTyCon tyCon -> TClass + | isTypeSynonymTyCon tyCon -> TTypeSyn + | isTypeFamilyTyCon tyCon -> TTypeFamily + | otherwise -> TNothing + ACoAxiom _ -> TNothing ----------------------- ---- the api ----------------------- -computeSemanticTokens :: NormalizedFilePath -> Action (Maybe SemanticTokens) -computeSemanticTokens nfp = +computeSemanticTokens :: IdeState -> NormalizedFilePath -> Action (Maybe SemanticTokens) +computeSemanticTokens state nfp = + let dbg = logWith state in runMaybeT $ do -- HAR{hieAst, refMap} <- MaybeT $ use GetHieAst nfp - HAR{..} <- MaybeT $ use GetHieAst nfp + [HAR{..}] <- usesMT GetHieAst [nfp] + -- [TcModuleResult{..}]<- usesMT TypeCheck [nfp] + [hscEnv -> hsc] <- usesMT (GhcSessionDeps_ True) [nfp] + -- HAR{..} <- MaybeT $ useWithStaleFastMT GetHieAst nfp liftIO $ putStrLn $ "moduleName: " <> showSDocUnsafe (ppr hieModule) - -- (ImportMap imports, _) <- MaybeT $ useWithStaleFastMT GetImportMap file - source :: ByteString <- lift $ getSourceFileSource nfp let xs = Map.toList $ getAsts hieAst - liftIO $ print $ "hieAst size: " <> show (List.length xs) + liftIO $ putStrLn $ "hieAst size: " <> show (List.length xs) case xs of ((_,ast):_) -> do - binds <- MaybeT $ use GetBindings nfp + -- compute imported names from hieAst let importedNames = importedNameFromModule hieModule ast - ShakeExtras{withHieDb} <- MaybeT $ fmap Just getShakeExtras - nameRefPaths <- getNamesRefs withHieDb importedNames - importedModuleNameSemanticMap <- MaybeT $ fmap Just $ computeImportedSemanticTokens nameRefPaths + -- accumulate names from typechecked module + -- km <- liftIO $ foldrM (getType hsc) (tcg_type_env tmrTypechecked) importedNames + km <- liftIO $ foldrM (getType hsc) emptyNameEnv importedNames + let importedModuleNameSemanticMap = Map.fromList $ flip mapMaybe (Set.toList importedNames) $ \name -> do + ty <- lookupNameEnv km name + return (name, tyThingSemantic ty) + liftIO $ forM (Set.toList importedNames) $ \name -> do + let ty = lookupNameEnv km name + dbg $ "imported name: " + <> showSDocUnsafe (ppr name) + <> " :: " <> showSDocUnsafe (ppr ty) + -- return (name, tyThingSemantic ty) + ShakeExtras{..} <- MaybeT $ fmap Just getShakeExtras let originalModuleNameSemanticMap = toNameSemanticMap refMap let combineMap = Map.unionWith (<>) originalModuleNameSemanticMap importedModuleNameSemanticMap let names = identifierGetter ast + + source :: ByteString <- lift $ getSourceFileSource nfp let moduleAbsTks = extractSemanticTokensFromNames combineMap names case semanticTokenAbsoluteSemanticTokens moduleAbsTks of Right tokens -> do - liftIO $ mapM_ (\x -> mapM_ print x) $ recoverSemanticTokens (bytestringString source) tokens + liftIO $ mapM_ (\x -> mapM_ (dbg . show) x) $ recoverSemanticTokens (bytestringString source) tokens pure tokens Left err -> do liftIO $ putStrLn $ "computeSemanticTokens: " <> show err MaybeT . pure $ Nothing _ -> MaybeT . pure $ Nothing + where + getType env n nameMap + | Nothing <- lookupNameEnv nameMap n + = do kind <- lookupKind env n + pure $ maybe nameMap (extendNameEnv nameMap n) kind + | otherwise = pure nameMap + lookupKind :: HscEnv -> Name -> IO (Maybe TyThing) + lookupKind env = fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env semanticTokensFull :: PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull @@ -130,14 +242,15 @@ semanticTokensFull state _ param = do let dbg = logWith state nfp <- getNormalizedFilePathE (param ^. (L.textDocument . L.uri)) dbg $ "semanticTokensFull: " <> show nfp + -- source :: ByteString <- lift $ getSourceFileSource nfp items <- liftIO $ runAction "SemanticTokens.semanticTokensFull" state - $ computeSemanticTokens nfp + $ computeSemanticTokens state nfp case items of Nothing -> pure $ InR Null Just items -> do - -- content <- liftIO $ readFile $ fromNormalizedFilePath nfp - -- liftIO $ mapM_ (mapM_ print) $ recoverSemanticTokens content items + content <- liftIO $ readFile $ fromNormalizedFilePath nfp + -- mapM_ (mapM_ (dbg . show)) $ recoverSemanticTokens content items pure $ InL items @@ -167,11 +280,10 @@ dataActualToken xs = maybe decodeError (Right . fmap semanticTokenAbsoluteActual -- span: /Users/ares/src/test/lib/SemanticTokens/Types.hs:(34,12)-(38,3) -- type RefMap a = M.Map Identifier [(Span, IdentifierDetails a)] -computeImportedSemanticTokens :: [NormalizedFilePath] -> Action NameSemanticMap -computeImportedSemanticTokens namPathMap = do - x <- forM namPathMap $ \nfp -> runMaybeT $ do - HAR{hieAst, refMap} <- MaybeT $ use GetHieAst nfp - binds <- MaybeT $ use GetBindings nfp - return $ toNameSemanticMap refMap - let xs = catMaybes x - return $ Map.unionsWith (<>) xs +computeImportedSemanticTokens :: IdeState -> [NormalizedFilePath] -> Set.Set Name -> MaybeT Action NameSemanticMap +computeImportedSemanticTokens state nfps names = + let dbg = logWith state in do + dbg "heelo" + let nameList = Set.toList names + let moduleNamePairs = [(1, nameOccName name) | name <- nameList] + return Map.empty diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs index 968ebd1246..fb3e7d47e0 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs @@ -43,9 +43,13 @@ import Ide.Plugin.SemanticTokens.Types import Language.LSP.Protocol.Types -- import HieDb.Types (DefRow (..)) import Data.Either (rights) +import qualified Data.HashSet as HashSet +import Data.Tuple (swap) import Development.IDE (filePathToUri') import Development.IDE.GHC.Error (positionToRealSrcLoc) import Development.IDE.Spans.LocalBindings (Bindings) +import Development.IDE.Types.Exports (ExportsMap (getModuleExportsMap), + IdentInfo) import Development.IDE.Types.Location (toNormalizedFilePath') @@ -54,13 +58,13 @@ import Development.IDE.Types.Location (toNormalizedFilePath') ----------------------------------------- ---- construct definition map from HieAST a ----------------------------------------- - -- do not use refMap from useAsts to get identifiers -- because it may contain ghc generated names or derived names -- which are not useful for semantic tokens (since they are not in source code) -- only use identifier both None derived and from source code identifierGetter :: HieAST a -> [(Name, Span)] -identifierGetter ast = getIds ast ++ concatMap identifierGetter (nodeChildren ast) +identifierGetter ast = if null (nodeChildren ast) then + getIds ast else concatMap identifierGetter (nodeChildren ast) where getIds :: HieAST a -> [(Name, Span)] getIds ast = [(c, nodeSpan ast) @@ -82,34 +86,36 @@ identifierGetter ast = getIds ast ++ concatMap identifierGetter (nodeChildren as -collectToken :: IdentifierDetails a -> Maybe SemanticTokenType -collectToken details = case NE.nonEmpty $ Set.toList $ identInfo details of +detailSemanticMaybeTokenType :: IdentifierDetails a -> Maybe SemanticTokenType +detailSemanticMaybeTokenType details = case NE.nonEmpty $ Set.toList $ identInfo details of Just infos -> Just $ List.maximum $ NE.map infoTokenType infos Nothing -> Nothing infoTokenType :: ContextInfo -> SemanticTokenType infoTokenType x = case x of - Use -> TNothing - MatchBind -> TNothing - IEThing _ -> TNothing -- todo find a way to get imported name - TyDecl -> TNothing -- type signature - - ValBind bt _ _ -> TValBind - PatternBind _ _ _ -> TPatternBind - ClassTyDecl _ -> TClassMethod - TyVarBind _ _ -> TTypeVariable - RecField _ _ -> TRecField + Use -> TNothing + MatchBind -> TNothing + IEThing _ -> TNothing -- todo find a way to get imported name + TyDecl -> TNothing -- type signature + + ValBind RegularBind _ _ -> TValBind + ValBind InstanceBind _ _ -> TClassMethod + PatternBind _ _ _ -> TPatternBind + ClassTyDecl _ -> TClassMethod + TyVarBind _ _ -> TTypeVariable + RecField _ _ -> TRecField -- data constructor, type constructor, type synonym, type family - Decl ClassDec _ -> TClass - Decl DataDec _ -> TTypeCon - Decl ConDec _ -> TDataCon - Decl SynDec _ -> TTypeSyn - Decl FamDec _ -> TTypeFamily + Decl ClassDec _ -> TClass + Decl DataDec _ -> TTypeCon + Decl ConDec _ -> TDataCon + Decl SynDec _ -> TTypeSyn + Decl FamDec _ -> TTypeFamily -- instance dec is class method - Decl InstDec _ -> TClassMethod - Decl PatSynDec _ -> TPatternSyn - EvidenceVarUse -> TNothing - EvidenceVarBind _ _ _ -> TNothing + Decl InstDec _ -> TClassMethod + Decl PatSynDec _ -> TPatternSyn + + EvidenceVarUse -> TNothing + EvidenceVarBind _ _ _ -> TNothing type NameSemanticMap = Map Name SemanticTokenType @@ -122,14 +128,19 @@ toNameSemanticMap rm = Map.fromListWith (<>) [ (name, tokenType) | (Right name, details) <- Map.toList rm , (_, detail) <- details - , let tokenType = collectToken detail + , let tokenType = detailSemanticMaybeTokenType detail , (Just tokenType) <- [tokenType] ] semanticTokenAbsoluteSemanticTokens :: [SemanticTokenAbsolute] -> Either Text SemanticTokens semanticTokenAbsoluteSemanticTokens xs = makeSemanticTokens defaultSemanticTokensLegend . List.sort $ xs extractSemanticTokensFromNames :: NameSemanticMap -> [(Name, Span)] -> [SemanticTokenAbsolute] -extractSemanticTokensFromNames nsm = map (uncurry toAbsSemanticToken) . mapMaybe (getSemantic nsm) +extractSemanticTokensFromNames nsm = + map (uncurry toAbsSemanticToken) . removeDup . mapMaybe (getSemantic nsm) + where + removeDup :: [(Span, SemanticTokenType)] -> [(Span, SemanticTokenType)] + removeDup xs = Map.toList $ Map.fromListWith (<>) xs + toAbsSemanticToken :: Span -> SemanticTokenType -> SemanticTokenAbsolute toAbsSemanticToken loc tokenType = @@ -150,8 +161,9 @@ getSemantic nameMap locName = do importedNameFromModule :: forall a. Module -> HieAST a -> Set.Set Name importedNameFromModule mod ast = Set.fromList importedNames where locatedNames = identifierGetter ast - importedNames = [name | (name, _) <- locatedNames, not $ nameIsLocalOrFrom mod name - , Just mod <- [nameModule_maybe name]] + -- importedNames = [name | (name, _) <- locatedNames, not $ nameIsLocalOrFrom mod name + -- , Just mod <- [nameModule_maybe name]] + importedNames = [name | (name, _) <- locatedNames] showLocatedNames :: [LIdP GhcRn] -> String @@ -159,6 +171,8 @@ showLocatedNames xs = unlines [ showSDocUnsafe (ppr locName) ++ " " ++ show (getLoc locName) | locName <- xs] +showName :: Name -> String +showName name = occNameString (occName name) <> ":" <> showSDocUnsafe (ppr name) getNamesRefs :: (MonadIO m) @@ -166,14 +180,17 @@ getNamesRefs -> Set.Set Name -> m [NormalizedFilePath] getNamesRefs withHieDb nameSet = do + -- let nameSpanMap = Map.toList $ Map.fromListWith (<>) $ (fmap . fmap) return nameSet refs <- fmap rights $ forM (Set.toList nameSet) $ \name -> do ans <- liftIO $ withHieDb (\hieDb -> findOneDef hieDb (nameOccName name) Nothing Nothing) return $ ans -- only those names that are in source code - return [path | ref <- refs, let (Just path) = rowToLoc ref] + let ans = [path | ref <- refs, let (Just path) = rowToLoc ref] +-- liftIO $ putStrLn $ "getNamesRefs: " <> show (List.length ans) +-- liftIO $ mapM_ (putStrLn . showName) nameSet +-- liftIO $ mapM_ print ans + return ans where rowToLoc (row:.info) = mfile where mfile = toNormalizedFilePath <$> modInfoSrcFile info - - diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 6f7e434046..92985ce630 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -36,6 +36,7 @@ import qualified Test.Hls (PluginTestDescriptor, import qualified Test.Hls.FileSystem as FS -- import Development.IDE.Plugin.Test import Control.Lens hiding (use) +import Control.Monad (forM) import qualified Data.List as List import Data.Maybe (fromJust) import qualified Data.Set as Set @@ -85,6 +86,61 @@ runSessionWithServerInDirAndGetSemantic file x = res <- Test.getSemanticTokens doc x res doc +semanticTokensClassTests :: TestTree +semanticTokensClassTests = testGroup "type class" + [ + + testCase "type class" $ do + let filePath = "./test/testdata/class.hs" + content <- liftIO $ Prelude.readFile filePath + let expect = + [SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 7, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 4, startChar = 11, len = 1}, name = "a"} + ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 3, len = 3}, name = "foo"} + ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 10, len = 1}, name = "a"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 15, len = 3}, name = "Int"}] + runSessionWithServerInDirAndGetSemantic "class.hs" $ \res doc -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _ -> error "No tokens found" + , testCase "imported class method InstanceClassMethodBind" $ do + let filePath = "./test/testdata/InstanceClassMethodBind.hs" + content <- liftIO $ Prelude.readFile filePath + let expect = + [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 16, len = 3}, name = "Int"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 10, len = 2}, name = "Eq"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 6, startChar = 5, len = 4}, name = "(==)"} + ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 6, startChar = 12, len = 9}, name = "undefined"}] + runSessionWithServerInDirAndGetSemantic "InstanceClassMethodBind.hs" $ \res doc -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _ -> error "No tokens found" + , + testCase "imported class method InstanceClassMethodUse" $ do + let filePath = "./test/testdata/InstanceClassMethodUse.hs" + content <- liftIO $ Prelude.readFile filePath + let expect = [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 2}, name = "go"} + ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 4, startChar = 9, len = 4}, name = "(==)"}] + runSessionWithServerInDirAndGetSemantic "InstanceClassMethodUse.hs" $ \res doc -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _ -> error "No tokens found" + ] + semanticTokensTests :: TestTree semanticTokensTests = @@ -104,7 +160,7 @@ semanticTokensTests = content <- liftIO $ Prelude.readFile filePath let expect = [ SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 5}, name = "hello"} - , SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 10, len = 6}, name = "String"} + , SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 4, startChar = 10, len = 6}, name = "String"} , SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 5}, name = "hello"} ] runSessionWithServerInDirAndGetSemantic "valBind.hs" $ \res doc -> do @@ -132,16 +188,13 @@ semanticTokensTests = (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () _ -> error "No tokens found" - , testCase "type class" $ do - let filePath = "./test/testdata/class.hs" + + , testCase "pattern match" $ do + let filePath = "./test/testdata/patternMatch.hs" content <- liftIO $ Prelude.readFile filePath - let expect = - [SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 7, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 4, startChar = 11, len = 1}, name = "a"} - ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 3, len = 3}, name = "foo"} - ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 10, len = 1}, name = "a"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 15, len = 3}, name = "Int"}] - runSessionWithServerInDirAndGetSemantic "class.hs" $ \res doc -> do + let expect = [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 1}, name = "g"} + ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 4, len = 7}, name = "Nothing"}] + runSessionWithServerInDirAndGetSemantic "patternMatch.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do @@ -161,14 +214,16 @@ semanticTokensTests = ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 5, startChar = 3, len = 1}, name = "x"} ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 5, startChar = 5, len = 1}, name = "y"} ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 5, startChar = 9, len = 1}, name = "x"} - ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 11, len = 1}, name = "+"} + ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 11, len = 1}, name = "+"} ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 5, startChar = 13, len = 1}, name = "y"}] runSessionWithServerInDirAndGetSemantic "patternBind.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + (\ xs -> liftIO $ forM (Prelude.zip xs expect) $ \(x, y) -> do + x @?= y + return ()) $ recoverSemanticTokens content tokens return () _ -> error "No tokens found" , testCase "pattern syn" $ do @@ -189,7 +244,7 @@ semanticTokensTests = content <- liftIO $ Prelude.readFile filePath let expect = [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 1}, name = "a"} - ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 7, len = 1}, name = "+"} + ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 4, startChar = 7, len = 1}, name = "+"} ] runSessionWithServerInDirAndGetSemantic "imported.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc @@ -200,6 +255,23 @@ semanticTokensTests = return () _ -> error "No tokens found" + , testCase "datatype" $ do + let filePath = "./test/testdata/datatype.hs" + content <- liftIO $ Prelude.readFile filePath + let expect = + [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 3, startChar = 12, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 16, len = 3}, name = "Int"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 30, len = 2}, name = "Eq"}] + runSessionWithServerInDirAndGetSemantic "datatype.hs" $ \res doc -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _ -> error "No tokens found" + , testCase "module import test" $ do let filePath1 = "./test/testdata/imported.hs" @@ -232,4 +304,4 @@ semanticTokensTests = main :: IO () main = defaultTestRunner $ testGroup "Semantic tokens" - [ semanticTokensTests ] + [semanticTokensTests, semanticTokensClassTests] diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/datatype.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/datatype.hs new file mode 100644 index 0000000000..8ca0c1b2dd --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/datatype.hs @@ -0,0 +1,3 @@ +module DataType where + +data Foo = Foo Int deriving (Eq) diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodBind.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodBind.hs new file mode 100644 index 0000000000..19a422e0fc --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodBind.hs @@ -0,0 +1,6 @@ +module InstanceClassMethodBind where + + +data Foo = Foo Int +instance Eq Foo where + (==) = undefined diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodUse.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodUse.hs new file mode 100644 index 0000000000..acce9bf224 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodUse.hs @@ -0,0 +1,5 @@ +module InstanceClassMethodUse where + + +go = (==) + diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/patternMatch.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/patternMatch.hs new file mode 100644 index 0000000000..25df18127e --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/patternMatch.hs @@ -0,0 +1,6 @@ +module PatternMatch where + + +g (Nothing, _) = 1 + + diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/patternbind.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/patternbind.hs index 37d9f2e029..c188b5c7b3 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/patternbind.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/patternbind.hs @@ -6,3 +6,4 @@ f x y = x + y + From 915e001513a53ba3b7f45c21c14802457cf81ec5 Mon Sep 17 00:00:00 2001 From: Patrick Date: Wed, 13 Dec 2023 05:30:13 +0800 Subject: [PATCH 13/73] Fix dependencies version --- .../hls-semantic-tokens-plugin.cabal | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal index a0e0a1ec64..cd2ec53429 100644 --- a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal +++ b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-semantic-tokens-plugin -version: 2.4.0.0 +version: 2.5.0.0 synopsis: Call hierarchy plugin for Haskell Language Server description: Please see the README on GitHub at @@ -35,8 +35,8 @@ library , containers , extra , hiedb - , ghcide == 2.4.0.0 - , hls-plugin-api == 2.4.0.0 + , ghcide == 2.5.0.0 + , hls-plugin-api == 2.5.0.0 , lens , lsp >=2.3 , sqlite-simple @@ -64,7 +64,7 @@ test-suite tests , extra , filepath , hls-semantic-tokens-plugin - , hls-test-utils == 2.4.0.0 + , hls-test-utils == 2.5.0.0 , ghcide-test-utils , hls-plugin-api , lens @@ -74,5 +74,5 @@ test-suite tests , text , data-default , bytestring - , ghcide == 2.4.0.0 - , hls-plugin-api == 2.4.0.0 + , ghcide == 2.5.0.0 + , hls-plugin-api == 2.5.0.0 From 65e2abd59ffc4fd9aac0e00217149343c8aee0b4 Mon Sep 17 00:00:00 2001 From: Patrick Date: Wed, 13 Dec 2023 21:15:01 +0800 Subject: [PATCH 14/73] fix version --- haskell-language-server.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 9ac8d5487d..108ef5172f 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -340,7 +340,7 @@ common refactor common semanticTokens if flag(semanticTokens) - build-depends: hls-semantic-tokens-plugin == 2.4.0.0 + build-depends: hls-semantic-tokens-plugin == 2.5.0.0 cpp-options: -Dhls_semanticTokens From 700fd81c92c18628539ee8935675a91633185b86 Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 14 Dec 2023 06:03:12 +0800 Subject: [PATCH 15/73] Retrieve nameSet from renamedSource to prevent names not visible(Such as by instance deriving) being handled --- .../hls-semantic-tokens-plugin.cabal | 3 +- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 205 ++++-------- .../src/Ide/Plugin/SemanticTokens/Types.hs | 44 +-- .../src/Ide/Plugin/SemanticTokens/query.hs | 129 ++++---- .../hls-semantic-tokens-plugin/test/Main.hs | 296 ++++++++++-------- .../test/testdata/classImportedDeriving.hs | 10 + .../test/testdata/imported.hs | 5 - .../test/testdata/typefamily.hs | 6 + .../test/testdata/valBind.hs | 4 +- 9 files changed, 330 insertions(+), 372 deletions(-) create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/classImportedDeriving.hs delete mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/imported.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/typefamily.hs diff --git a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal index cd2ec53429..da1fd649c5 100644 --- a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal +++ b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal @@ -31,7 +31,7 @@ library hs-source-dirs: src build-depends: , aeson - , base >=4.12 && <5 + , base , containers , extra , hiedb @@ -75,4 +75,5 @@ test-suite tests , data-default , bytestring , ghcide == 2.5.0.0 + -- , ghcide == 2.5.0.0 , hls-plugin-api == 2.5.0.0 diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index fec03523a0..2d63b2bae0 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -69,6 +69,7 @@ import qualified Data.Set as Set import Data.Text (Text) import Data.Traversable (for) import Data.Typeable (cast) +import Debug.Trace (trace) import Development.IDE (IdeAction, IdeState, Priority (..), ideLogger, logPriority, use, uses) @@ -83,80 +84,15 @@ import Development.IDE.Types.Exports (ExportsMap (..), import Development.IDE.Types.HscEnvEq (hscEnv) import GHC.Conc (readTVar) --- logWith :: (MonadIO m) => IdeState -> String -> m () --- logWith st = liftIO . logPriority (ideLogger st) Info . T.pack . show - logWith :: (MonadIO m) => IdeState -> String -> m () -logWith st = liftIO . print - -bytestringString :: ByteString -> String -bytestringString = map (toEnum . fromEnum) . unpack - --- data TyThing --- = AnId Id --- | AConLike ConLike --- | ATyCon TyCon -- TyCons and classes; see Note [ATyCon for classes] --- | ACoAxiom (CoAxiom Branched) --- a :: IdDetails --- a = undefined - --- | Identifier Details --- --- The 'IdDetails' of an 'Id' give stable, and necessary, --- information about the Id. --- data IdDetails --- = VanillaId +logWith st = liftIO . logPriority (ideLogger st) Info . T.pack --- -- | The 'Id' for a record selector --- | RecSelId --- { sel_tycon :: RecSelParent --- , sel_naughty :: Bool -- True <=> a "naughty" selector which can't actually exist, for example @x@ in: --- -- data T = forall a. MkT { x :: a } --- } -- See Note [Naughty record selectors] in GHC.Tc.TyCl --- | DataConWorkId DataCon -- ^ The 'Id' is for a data constructor /worker/ --- | DataConWrapId DataCon -- ^ The 'Id' is for a data constructor /wrapper/ - --- -- [the only reasons we need to know is so that --- -- a) to support isImplicitId --- -- b) when desugaring a RecordCon we can get --- -- from the Id back to the data con] --- | ClassOpId Class -- ^ The 'Id' is a superclass selector, --- -- or class operation of a class - --- | PrimOpId PrimOp Bool -- ^ The 'Id' is for a primitive operator --- -- True <=> is representation-polymorphic, --- -- and hence has no binding --- -- This lev-poly flag is used only in GHC.Types.Id.hasNoBinding - --- | FCallId ForeignCall -- ^ The 'Id' is for a foreign call. --- -- Type will be simple: no type families, newtypes, etc - --- | TickBoxOpId TickBoxOp -- ^ The 'Id' is for a HPC tick box (both traditional and binary) - --- | DFunId Bool -- ^ A dictionary function. --- -- Bool = True <=> the class has only one method, so may be --- -- implemented with a newtype, so it might be bad --- -- to be strict on this dictionary +-- logWith :: (MonadIO m) => IdeState -> String -> m () +-- logWith st = liftIO . print --- | CoVarId -- ^ A coercion variable --- -- This only covers /un-lifted/ coercions, of type --- -- (t1 ~# t2) or (t1 ~R# t2), not their lifted variants --- | JoinId JoinArity (Maybe [CbvMark]) --- -- ^ An 'Id' for a join point taking n arguments --- -- Note [Join points] in "GHC.Core" --- -- Can also work as a WorkerLikeId if given `CbvMark`s. --- -- See Note [CBV Function Ids] --- -- The [CbvMark] is always empty (and ignored) until after Tidy. --- | WorkerLikeId [CbvMark] --- -- ^ An 'Id' for a worker like function, which might expect some arguments to be --- -- passed both evaluated and tagged. --- -- Worker like functions are create by W/W and SpecConstr and we can expect that they --- -- aren't used unapplied. --- -- See Note [CBV Function Ids] --- -- See Note [Tag Inference] --- -- The [CbvMark] is always empty (and ignored) until after Tidy for ids from the current --- -- module. +bytestringString :: ByteString -> String +bytestringString = map (toEnum . fromEnum) . unpack tyThingSemantic :: TyThing -> SemanticTokenType tyThingSemantic ty = case ty of @@ -171,11 +107,11 @@ tyThingSemantic ty = case ty of RealDataCon _ -> TDataCon PatSynCon _ -> TPatternSyn ATyCon tyCon - | isDataTyCon tyCon -> TTypeCon - | isPrimTyCon tyCon -> TTypeCon - | isClassTyCon tyCon -> TClass | isTypeSynonymTyCon tyCon -> TTypeSyn | isTypeFamilyTyCon tyCon -> TTypeFamily + | isClassTyCon tyCon -> TClass + | isDataTyCon tyCon -> TTypeCon + | isPrimTyCon tyCon -> TTypeCon | otherwise -> TNothing ACoAxiom _ -> TNothing @@ -185,48 +121,40 @@ tyThingSemantic ty = case ty of computeSemanticTokens :: IdeState -> NormalizedFilePath -> Action (Maybe SemanticTokens) computeSemanticTokens state nfp = - let dbg = logWith state in - runMaybeT $ do - -- HAR{hieAst, refMap} <- MaybeT $ use GetHieAst nfp - [HAR{..}] <- usesMT GetHieAst [nfp] - -- [TcModuleResult{..}]<- usesMT TypeCheck [nfp] - [hscEnv -> hsc] <- usesMT (GhcSessionDeps_ True) [nfp] - -- HAR{..} <- MaybeT $ useWithStaleFastMT GetHieAst nfp - liftIO $ putStrLn $ "moduleName: " <> showSDocUnsafe (ppr hieModule) - let xs = Map.toList $ getAsts hieAst - liftIO $ putStrLn $ "hieAst size: " <> show (List.length xs) - - case xs of - ((_,ast):_) -> do - -- compute imported names from hieAst - let importedNames = importedNameFromModule hieModule ast - -- accumulate names from typechecked module - -- km <- liftIO $ foldrM (getType hsc) (tcg_type_env tmrTypechecked) importedNames - km <- liftIO $ foldrM (getType hsc) emptyNameEnv importedNames - let importedModuleNameSemanticMap = Map.fromList $ flip mapMaybe (Set.toList importedNames) $ \name -> do - ty <- lookupNameEnv km name - return (name, tyThingSemantic ty) - liftIO $ forM (Set.toList importedNames) $ \name -> do - let ty = lookupNameEnv km name - dbg $ "imported name: " - <> showSDocUnsafe (ppr name) - <> " :: " <> showSDocUnsafe (ppr ty) - -- return (name, tyThingSemantic ty) - ShakeExtras{..} <- MaybeT $ fmap Just getShakeExtras - let originalModuleNameSemanticMap = toNameSemanticMap refMap - let combineMap = Map.unionWith (<>) originalModuleNameSemanticMap importedModuleNameSemanticMap - let names = identifierGetter ast - + runMaybeT $ do + -- let dbg = logWith state + -- let getAst HAR{hieAst, refMap} = hieAst + (HAR{hieAst, refMap, hieModule}, _) <- useWithStaleMT GetHieAst nfp + (_, ast) <- MaybeT $ return $ listToMaybe $ Map.toList $ getAsts hieAst + (TcModuleResult{..}, _) <- useWithStaleMT TypeCheck nfp + (hscEnv -> hsc, _) <- useWithStaleMT GhcSessionDeps nfp + -- because the names get from ast might contain derived name + let nameSet = nameGetter tmrRenamed + -- let nameSet = hieAstNameSet ast + let names = hieAstSpanNames ast + + -- ask hscEnv for none local types + km <- liftIO $ foldrM (getType hsc) (tcg_type_env tmrTypechecked) nameSet + -- name from type typecheck + let importedModuleNameSemanticMap = Map.fromList $ flip mapMaybe (Set.toList nameSet) $ \name -> do + ty <- lookupNameEnv km name + return (name, tyThingSemantic ty) + let localNameSemanticMap = toNameSemanticMap $ Map.filterWithKey (\k _ -> + either (const False) (flip Set.member nameSet) k) refMap + let combineMap = Map.unionWith (<>) localNameSemanticMap importedModuleNameSemanticMap + -- print all names + -- deriving method locate in the same position as the class name + -- liftIO $ mapM_ (\ (name, tokenType) -> dbg ("debug semanticMap: " <> showClearName name <> ":" <> show tokenType )) $ Map.toList importedModuleNameSemanticMap + -- liftIO $ mapM_ (\ (span, name) -> dbg ("debug names: " <> showClearName name <> ":" <> printCompactRealSrc span ) ) names + let moduleAbsTks = extractSemanticTokensFromNames combineMap names + case semanticTokenAbsoluteSemanticTokens moduleAbsTks of + Right tokens -> do source :: ByteString <- lift $ getSourceFileSource nfp - let moduleAbsTks = extractSemanticTokensFromNames combineMap names - case semanticTokenAbsoluteSemanticTokens moduleAbsTks of - Right tokens -> do - liftIO $ mapM_ (\x -> mapM_ (dbg . show) x) $ recoverSemanticTokens (bytestringString source) tokens - pure tokens - Left err -> do - liftIO $ putStrLn $ "computeSemanticTokens: " <> show err - MaybeT . pure $ Nothing - _ -> MaybeT . pure $ Nothing + -- liftIO $ mapM_ (\x -> mapM_ (dbg . show) x) $ recoverSemanticTokens (bytestringString source) tokens + pure tokens + Left err -> do + liftIO $ putStrLn $ "computeSemanticTokens: " <> show err + MaybeT . pure $ Nothing where getType env n nameMap | Nothing <- lookupNameEnv nameMap n @@ -239,9 +167,9 @@ computeSemanticTokens state nfp = semanticTokensFull :: PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull semanticTokensFull state _ param = do - let dbg = logWith state + -- let dbg = logWith state nfp <- getNormalizedFilePathE (param ^. (L.textDocument . L.uri)) - dbg $ "semanticTokensFull: " <> show nfp + -- dbg $ "semanticTokensFull: " <> show nfp -- source :: ByteString <- lift $ getSourceFileSource nfp items <- liftIO $ runAction "SemanticTokens.semanticTokensFull" state @@ -258,32 +186,25 @@ semanticTokensFull state _ param = do ---- recover tokens ----------------------- +-- | recoverSemanticTokens +-- used for debug and test +-- this function is used to recover the original tokens(with token in haskell token type zoon) +-- from the lsp semantic tokens(with token in lsp token type zoon) recoverSemanticTokens :: String -> SemanticTokens -> Either Text [SemanticTokenOriginal] recoverSemanticTokens sourceCode (SemanticTokens _ xs) = fmap (fmap $ tokenOrigin sourceCode) $ dataActualToken xs - - -tokenOrigin :: [Char] -> ActualToken -> SemanticTokenOriginal -tokenOrigin sourceCode (line, startChar, len, tokenType, _) = - -- convert back to count from 1 - SemanticTokenOriginal tokenType (Loc (line+1) (startChar+1) len) name - where tLine = lines sourceCode !? fromIntegral line - name = maybe "no source" (take (fromIntegral len) . drop (fromIntegral startChar)) tLine - - -dataActualToken :: [UInt] -> Either Text [ActualToken] -dataActualToken xs = maybe decodeError (Right . fmap semanticTokenAbsoluteActualToken . absolutizeTokens) - $ mapM fromTuple (chunksOf 5 $ map fromIntegral xs) where - decodeError = Left "recoverSemanticTokenRelative: wrong token data" - fromTuple [a, b, c, d, _] = Just $ SemanticTokenRelative a b c (fromInt $ fromIntegral d) [] - fromTuple _ = Nothing - --- span: /Users/ares/src/test/lib/SemanticTokens/Types.hs:(34,12)-(38,3) --- type RefMap a = M.Map Identifier [(Span, IdentifierDetails a)] -computeImportedSemanticTokens :: IdeState -> [NormalizedFilePath] -> Set.Set Name -> MaybeT Action NameSemanticMap -computeImportedSemanticTokens state nfps names = - let dbg = logWith state in do - dbg "heelo" - let nameList = Set.toList names - let moduleNamePairs = [(1, nameOccName name) | name <- nameList] - return Map.empty + tokenOrigin :: [Char] -> ActualToken -> SemanticTokenOriginal + tokenOrigin sourceCode (line, startChar, len, tokenType, _) = + -- convert back to count from 1 + SemanticTokenOriginal tokenType (Loc (line+1) (startChar+1) len) name + where tLine = lines sourceCode !? fromIntegral line + name = maybe "no source" (take (fromIntegral len) . drop (fromIntegral startChar)) tLine + + + dataActualToken :: [UInt] -> Either Text [ActualToken] + dataActualToken xs = maybe decodeError (Right . fmap semanticTokenAbsoluteActualToken . absolutizeTokens) + $ mapM fromTuple (chunksOf 5 $ map fromIntegral xs) + where + decodeError = Left "recoverSemanticTokenRelative: wrong token data" + fromTuple [a, b, c, d, _] = Just $ SemanticTokenRelative a b c (fromInt $ fromIntegral d) [] + fromTuple _ = Nothing diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 6f81d0bfec..dd93b033c1 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -1,4 +1,6 @@ {-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE StrictData #-} + module Ide.Plugin.SemanticTokens.Types where @@ -127,22 +129,11 @@ instance Show ContextInfo where EvidenceVarUse -> "EvidenceVarUse" -printCompactSrcSpan :: SrcSpan -> String -printCompactSrcSpan (RealSrcSpan x _buf) = printCompactRealSrc x -printCompactSrcSpan x = "noSrc" printCompactRealSrc :: RealSrcSpan -> String printCompactRealSrc x = show (srcSpanStartLine x) <> ":" <> show (srcSpanStartCol x) <> "-" <> show (srcSpanEndCol x) - -type IdentifierItem = (Span, Name, NE.NonEmpty ContextInfo) -newtype NIdentifier = NIdentifier IdentifierItem - -identifierTokenType :: Identifier -> SemanticTokenType -identifierTokenType (Right x) = toTokenType x -identifierTokenType (Left x) = TNothing - toTokenType :: Name -> SemanticTokenType toTokenType locName = case occNameSpace $ occName locName of x | isDataConNameSpace x -> TDataCon @@ -151,24 +142,8 @@ toTokenType locName = case occNameSpace $ occName locName of x | isVarNameSpace x -> TValBind _ -> TNothing -instance Show NIdentifier where - show (NIdentifier (span, x, y)) = - occNameString (nameOccName x) - <> "&" <> (show $ break (==':') $ occNameString $ nameOccName x) - <> "&" <> (show $ toTokenType x) - <> " [" <> show y <> "]" - <> " nameSrc: "<> printCompactSrcSpan (nameSrcSpan x) <> " " <> printCompactRealSrc span -getOriginalTextFromId :: String -> NIdentifier -> String -getOriginalTextFromId sourceCode (NIdentifier (span, _, _)) = fromMaybe "" $ do - tLine <- lines sourceCode List.!? (line-1) - return $ take len $ drop (startChar-1) tLine - where - line = srcSpanStartLine span - startChar = srcSpanStartCol span - len= srcSpanEndCol span - startChar - ----------------------- ----- identifier token map @@ -188,3 +163,18 @@ showIdentifierDetails x = show $ identInfo x showIdentifier :: Identifier -> String showIdentifier (Left x) = showSDocUnsafe (ppr x) showIdentifier (Right x) = nameStableString x + +showLocatedNames :: [LIdP GhcRn] -> String +showLocatedNames xs = unlines + [ showSDocUnsafe (ppr locName) ++ " " ++ show (getLoc locName) + | locName <- xs] + +showClearName :: Name -> String +showClearName name = occNameString (occName name) <> ":" <> showSDocUnsafe (ppr name) <> ":" <> showNameType name + +showNameType :: Name -> String +showNameType name + | isInternalName name = "InternalName" + | isExternalName name = "ExternalName" + | isSystemName name = "SystemName" + | isWiredInName name = "WiredInName" diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs index fb3e7d47e0..029c90df49 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs @@ -8,7 +8,8 @@ {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeOperators #-} module Ide.Plugin.SemanticTokens.Query where -import Control.Arrow ((&&&)) +import Control.Arrow (ArrowChoice ((|||)), + (&&&)) import Control.Monad (forM) import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Char (isAlphaNum) @@ -18,14 +19,14 @@ import qualified Data.List as List import qualified Data.List.NonEmpty as NE import Data.Map (Map) import qualified Data.Map as Map -import Data.Maybe (catMaybes, listToMaybe, - mapMaybe) +import Data.Maybe (catMaybes, fromMaybe, + listToMaybe, mapMaybe) import Data.Ord (comparing) import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as Text import qualified Data.Text.Lazy.Builder as Text -import Development.IDE (Action, +import Development.IDE (Action, filePathToUri', rangeToRealSrcSpan, realSpan) import Development.IDE.GHC.Compat @@ -45,7 +46,7 @@ import Language.LSP.Protocol.Types import Data.Either (rights) import qualified Data.HashSet as HashSet import Data.Tuple (swap) -import Development.IDE (filePathToUri') +import Debug.Trace import Development.IDE.GHC.Error (positionToRealSrcLoc) import Development.IDE.Spans.LocalBindings (Bindings) import Development.IDE.Types.Exports (ExportsMap (getModuleExportsMap), @@ -55,25 +56,31 @@ import Development.IDE.Types.Location (toNormalizedFilePath') + +nameToCollect :: Name -> Set.Set Name +nameToCollect name = Set.singleton name + +nameGetter :: RenamedSource -> Set.Set Name +nameGetter = everything Set.union (Set.empty `mkQ` nameToCollect) + ----------------------------------------- ---- construct definition map from HieAST a ----------------------------------------- --- do not use refMap from useAsts to get identifiers --- because it may contain ghc generated names or derived names --- which are not useful for semantic tokens (since they are not in source code) --- only use identifier both None derived and from source code -identifierGetter :: HieAST a -> [(Name, Span)] -identifierGetter ast = if null (nodeChildren ast) then - getIds ast else concatMap identifierGetter (nodeChildren ast) +hieAstSpanNames :: HieAST a -> [(Span, Name)] +hieAstSpanNames ast = if null (nodeChildren ast) then + getIds ast else concatMap hieAstSpanNames (nodeChildren ast) where - getIds :: HieAST a -> [(Name, Span)] - getIds ast = [(c, nodeSpan ast) + getIds :: HieAST a -> [(Span, Name)] + getIds ast = [(nodeSpan ast, c) | (Right c, d) <- Map.toList $ getNodeIds' ast -- at least get one info - , let (Just infos) = NE.nonEmpty $ Set.toList $ identInfo d - -- , SourceInfo == getSourcedNodeInfo (sourcedNodeInfo ast) - , not $ isDerivedOccName (occName c) + , not $ any isEvidenceBind $ identInfo d + , not $ any isEvidenceUse $ identInfo d + , Set.size (identInfo d) > 0 + -- , not $ isDerivedOccName (occName c) ] + -- getNodeIds'' :: HieAST a -> Map.Map Identifier (IdentifierDetails a) + -- getNodeIds'' = maybeToList . getSourcedNodeInfo . sourcedNodeInfo getNodeIds' :: HieAST a -> Map.Map Identifier (IdentifierDetails a) getNodeIds' = Map.foldl' combineNodeIds Map.empty . Map.filterWithKey (\k _ -> k == SourceInfo) @@ -86,16 +93,12 @@ identifierGetter ast = if null (nodeChildren ast) then -detailSemanticMaybeTokenType :: IdentifierDetails a -> Maybe SemanticTokenType -detailSemanticMaybeTokenType details = case NE.nonEmpty $ Set.toList $ identInfo details of - Just infos -> Just $ List.maximum $ NE.map infoTokenType infos - Nothing -> Nothing infoTokenType :: ContextInfo -> SemanticTokenType infoTokenType x = case x of Use -> TNothing MatchBind -> TNothing - IEThing _ -> TNothing -- todo find a way to get imported name + IEThing _ -> TNothing TyDecl -> TNothing -- type signature ValBind RegularBind _ _ -> TValBind @@ -125,21 +128,31 @@ type NameSemanticMap = Map Name SemanticTokenType toNameSemanticMap :: RefMap a -> NameSemanticMap toNameSemanticMap rm = Map.fromListWith (<>) - [ (name, tokenType) + [ + -- trace ("toNameSemanticMap" <> ":" <> showSDocUnsafe (ppr name) <> " : " <> printCompactRealSrc span <> ":" <> showIdentifierDetails detail <> " : " <> show tokenType) + (name, tokenType) | (Right name, details) <- Map.toList rm - , (_, detail) <- details - , let tokenType = detailSemanticMaybeTokenType detail + , not $ isDerivedOccName (occName name) + , (span, detail) <- details + , let tokenType = detailSemanticMaybeTokenType $ identInfo detail , (Just tokenType) <- [tokenType] ] + where + detailSemanticMaybeTokenType :: Set.Set ContextInfo -> Maybe SemanticTokenType + detailSemanticMaybeTokenType details = case NE.nonEmpty $ Set.toList details of + Just infos -> Just $ maximum $ NE.map infoTokenType infos + Nothing -> Nothing + semanticTokenAbsoluteSemanticTokens :: [SemanticTokenAbsolute] -> Either Text SemanticTokens semanticTokenAbsoluteSemanticTokens xs = makeSemanticTokens defaultSemanticTokensLegend . List.sort $ xs -extractSemanticTokensFromNames :: NameSemanticMap -> [(Name, Span)] -> [SemanticTokenAbsolute] +extractSemanticTokensFromNames :: NameSemanticMap -> [(Span, Name)] -> [SemanticTokenAbsolute] extractSemanticTokensFromNames nsm = - map (uncurry toAbsSemanticToken) . removeDup . mapMaybe (getSemantic nsm) + map (uncurry toAbsSemanticToken) . mergeNameFromSamSpan . mapMaybe (getSemantic nsm) where - removeDup :: [(Span, SemanticTokenType)] -> [(Span, SemanticTokenType)] - removeDup xs = Map.toList $ Map.fromListWith (<>) xs + -- merge all tokens with same span + mergeNameFromSamSpan :: [(Span, SemanticTokenType)] -> [(Span, SemanticTokenType)] + mergeNameFromSamSpan xs = Map.toList $ Map.fromListWith (<>) xs toAbsSemanticToken :: Span -> SemanticTokenType -> SemanticTokenAbsolute @@ -150,47 +163,17 @@ toAbsSemanticToken loc tokenType = in SemanticTokenAbsolute (fromIntegral line) (fromIntegral startChar) (fromIntegral len) (toLspTokenType tokenType) [SemanticTokenModifiers_Declaration] -getSemantic :: Map Name SemanticTokenType -> (Name, Span) -> Maybe (Span, SemanticTokenType) -getSemantic nameMap locName = do - let name = fst locName - let span = snd locName - let tkt = toTokenType name - let tokenType = maybe tkt (\x -> tkt <> x) $ Map.lookup name nameMap - pure (span, tokenType) - -importedNameFromModule :: forall a. Module -> HieAST a -> Set.Set Name -importedNameFromModule mod ast = Set.fromList importedNames - where locatedNames = identifierGetter ast - -- importedNames = [name | (name, _) <- locatedNames, not $ nameIsLocalOrFrom mod name - -- , Just mod <- [nameModule_maybe name]] - importedNames = [name | (name, _) <- locatedNames] - - -showLocatedNames :: [LIdP GhcRn] -> String -showLocatedNames xs = unlines - [ showSDocUnsafe (ppr locName) ++ " " ++ show (getLoc locName) - | locName <- xs] - -showName :: Name -> String -showName name = occNameString (occName name) <> ":" <> showSDocUnsafe (ppr name) - -getNamesRefs - :: (MonadIO m) - => WithHieDb - -> Set.Set Name - -> m [NormalizedFilePath] -getNamesRefs withHieDb nameSet = do - --- let nameSpanMap = Map.toList $ Map.fromListWith (<>) $ (fmap . fmap) return nameSet - refs <- fmap rights $ forM (Set.toList nameSet) $ \name -> - do ans <- liftIO $ withHieDb (\hieDb -> findOneDef hieDb (nameOccName name) Nothing Nothing) - return $ ans --- only those names that are in source code - let ans = [path | ref <- refs, let (Just path) = rowToLoc ref] --- liftIO $ putStrLn $ "getNamesRefs: " <> show (List.length ans) --- liftIO $ mapM_ (putStrLn . showName) nameSet --- liftIO $ mapM_ print ans - return ans - where - rowToLoc (row:.info) = mfile - where mfile = toNormalizedFilePath <$> modInfoSrcFile info +getSemantic :: Map Name SemanticTokenType -> (Span, Name) -> Maybe (Span, SemanticTokenType) +getSemantic nameMap (span, name) = do + -- let tkt = toTokenType name + -- let tokenType = maybe tkt (\x -> tkt <> x) $ Map.lookup name nameMap + let tokenType = Map.lookup name nameMap + fmap (span,) tokenType + +-- this one might contain derived names +hieAstNameSet :: HieAST a -> Set.Set Name +hieAstNameSet ast = Set.fromList importedNames + where locatedNames = hieAstSpanNames ast + importedNames = [name | (_, name) <- locatedNames] + + diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 92985ce630..406d8b86fd 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -1,64 +1,44 @@ {-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} -{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE TypeApplications #-} -import Data.Bifunctor -import Data.ByteString as BS -import Data.Default -import qualified Data.Maybe -import Data.Text -import Development.IDE -import Development.IDE.GHC.Compat --- import Development.IDE.Test --- import Test.Hls --- import Test.Hls.Util (withCanonicalTempDir) + + import Control.Arrow (Arrow ((***)), (&&&), (+++)) -import Data.Data -import Data.Functor (void) -import Data.Map as Map -import Data.String (fromString) -import System.Environment.Blank -import System.FilePath --- import qualified Development.IDE.Core.Shake as Shake -import Ide.Types -import qualified Test.Hls (PluginTestDescriptor, - mkPluginTestDescriptor', - runSessionWithServerInTmpDir, - waitForAction) -import qualified Test.Hls.FileSystem as FS --- import Development.IDE.Plugin.Test import Control.Lens hiding (use) import Control.Monad (forM) +import Data.Bifunctor +import qualified Data.ByteString as BS +import Data.Data +import Data.Default +import Data.Functor (void) import qualified Data.List as List +import Data.Map as Map import Data.Maybe (fromJust) +import qualified Data.Maybe import qualified Data.Set as Set -import Development.IDE.Plugin.Test (WaitForIdeRuleResult (..)) +import Data.String (fromString) +import Data.Text import Ide.Plugin.Error (getNormalizedFilePathE) import Ide.Plugin.SemanticTokens import Ide.Plugin.SemanticTokens.Types +import Ide.Types import qualified Language.LSP.Protocol.Lens as L import Language.LSP.Protocol.Types (SemanticTokens (..), SemanticTokensParams (..)) import qualified Language.LSP.Test as Test -import Test.Hls -import Test.Hls (TextDocumentIdentifier, - getCodeLenses, openDoc, +import System.Environment.Blank +import System.FilePath +import qualified Test.Hls (PluginTestDescriptor, + mkPluginTestDescriptor', + runSessionWithServerInTmpDir, waitForAction) +import Test.Hls +import qualified Test.Hls.FileSystem as FS import Test.Hls.Util (withCanonicalTempDir) -getUniqueName :: (NamedThing a) => a -> Name -getUniqueName = getName - --- astToString :: (Data a) => a -> String --- astToString = showSDoc fakeDynFlags . showAstDataFull testDataDir :: FilePath testDataDir = "test" "testdata" @@ -71,7 +51,7 @@ semanticTokensPlugin = Test.Hls.mkPluginTestDescriptor' Ide.Plugin.SemanticToken mkSemanticTokensParams :: TextDocumentIdentifier -> SemanticTokensParams -mkSemanticTokensParams doc = SemanticTokensParams Nothing Nothing doc +mkSemanticTokensParams = SemanticTokensParams Nothing Nothing runSessionWithServerInDir file x = Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do @@ -86,11 +66,51 @@ runSessionWithServerInDirAndGetSemantic file x = res <- Test.getSemanticTokens doc x res doc -semanticTokensClassTests :: TestTree -semanticTokensClassTests = testGroup "type class" +runSessionWithServerInDirAndGetSemanticsFile file x = + Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do + let path = "." testDataDir file + content <- liftIO $ readFile path + doc <- openDoc file "haskell" + res <- waitForAction "TypeCheck" doc + res <- Test.getSemanticTokens doc + x res doc content + +semanticTokensImportedTests :: TestTree +semanticTokensImportedTests = testGroup "imported test" [ + testCase "type class" $ do + let expect = + [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} + , SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} + , SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 26, len = 4}, name = "Show"} + , SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 6, startChar = 1, len = 2}, name = "eq"} + , SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 6, startChar = 8, len = 2}, name = "Eq"} + , SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 11, len = 1}, name = "a"} + , SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 17, len = 1}, name = "a"} + , SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 22, len = 4}, name = "Bool"} + , SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 7, startChar = 1, len = 2}, name = "eq"} + , SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 7, startChar = 4, len = 1}, name = "b"} + , SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 7, startChar = 8, len = 1}, name = "b"} + , SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 7, startChar = 10, len = 2}, name = "=="} + , SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 7, startChar = 13, len = 1}, name = "b"}] - testCase "type class" $ do + + runSessionWithServerInDirAndGetSemanticsFile "class.hs" $ \res doc content -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found" + + + + ] + +semanticTokensClassTests :: TestTree +semanticTokensClassTests = testGroup "type class" + [ testCase "type class" $ do let filePath = "./test/testdata/class.hs" content <- liftIO $ Prelude.readFile filePath let expect = @@ -106,7 +126,7 @@ semanticTokensClassTests = testGroup "type class" either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () - _ -> error "No tokens found" + _notoken -> error "No tokens found" , testCase "imported class method InstanceClassMethodBind" $ do let filePath = "./test/testdata/InstanceClassMethodBind.hs" content <- liftIO $ Prelude.readFile filePath @@ -114,7 +134,7 @@ semanticTokensClassTests = testGroup "type class" [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 16, len = 3}, name = "Int"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 10, len = 2}, name = "Eq"} + ,SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 5, startChar = 10, len = 2}, name = "Eq"} ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Foo"} ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 6, startChar = 5, len = 4}, name = "(==)"} ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 6, startChar = 12, len = 9}, name = "undefined"}] @@ -125,7 +145,7 @@ semanticTokensClassTests = testGroup "type class" either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () - _ -> error "No tokens found" + _notokens -> error "No tokens found" , testCase "imported class method InstanceClassMethodUse" $ do let filePath = "./test/testdata/InstanceClassMethodUse.hs" @@ -138,57 +158,46 @@ semanticTokensClassTests = testGroup "type class" Just tokens -> do either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () - _ -> error "No tokens found" - ] - + _notokens -> error "No tokens found" -semanticTokensTests :: TestTree -semanticTokensTests = - testGroup - "get semantic Tokens" - [ - testCase "variable" $ do - -- let filePath = "./test/testdata/T1.hs" - runSessionWithServerInDirAndGetSemantic "T1.hs" $ \tokens _ -> do - case tokens ^? _L of - Just tokens -> do - liftIO $ 1 @?= 1 - _ -> error "No tokens found" - liftIO $ 1 @?= 1 - , testCase "value bind" $ do - let filePath = "./test/testdata/valBind.hs" + , testCase "imported deriving" $ do + let filePath = "./test/testdata/classImportedDeriving.hs" content <- liftIO $ Prelude.readFile filePath - let expect = [ - SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 5}, name = "hello"} - , SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 4, startChar = 10, len = 6}, name = "String"} - , SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 5}, name = "hello"} - ] - runSessionWithServerInDirAndGetSemantic "valBind.hs" $ \res doc -> do + let expect = + [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 26, len = 4}, name = "Show"}] + runSessionWithServerInDirAndGetSemantic "classImportedDeriving.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () - _ -> error "No tokens found" + _notokens -> error "No tokens found" + ] - , testCase "record" $ do - let filePath = "./test/testdata/record.hs" +semanticTokensValuePatternTests :: TestTree +semanticTokensValuePatternTests = + testGroup + "value and patterns " + [ + testCase "value bind" $ do + let filePath = "./test/testdata/valBind.hs" content <- liftIO $ Prelude.readFile filePath let expect = - [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 4, startChar = 18, len = 3}, name = "foo"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 25, len = 3}, name = "Int"}] - runSessionWithServerInDirAndGetSemantic "record.hs" $ \res doc -> do + [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 5}, name = "hello"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 10, len = 4}, name = "Char"} + ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 5}, name = "hello"} + ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 9, len = 4}, name = "head"}] + runSessionWithServerInDirAndGetSemantic "valBind.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () - _ -> error "No tokens found" - + _noTokens -> error "No tokens found" , testCase "pattern match" $ do let filePath = "./test/testdata/patternMatch.hs" content <- liftIO $ Prelude.readFile filePath @@ -201,8 +210,7 @@ semanticTokensTests = either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () - _ -> error "No tokens found" - + _noTokens -> error "No tokens found" , testCase "pattern bind" $ do let filePath = "./test/testdata/patternBind.hs" content <- liftIO $ Prelude.readFile filePath @@ -225,7 +233,21 @@ semanticTokensTests = x @?= y return ()) $ recoverSemanticTokens content tokens return () - _ -> error "No tokens found" + _notokens -> error "No tokens found" + ] + +semanticTokensTests :: TestTree +semanticTokensTests = + testGroup + "other semantic Token test" + [ + testCase "mixed parse test" $ do + -- let filePath = "./test/testdata/T1.hs" + runSessionWithServerInDirAndGetSemantic "T1.hs" $ \tokens _ -> do + case tokens ^? _L of + Just tokens -> liftIO $ 1 @?= 1 + _notokens -> error "No tokens found" + liftIO $ 1 @?= 1 , testCase "pattern syn" $ do let filePath = "./test/testdata/patternsyn.hs" content <- liftIO $ Prelude.readFile filePath @@ -238,31 +260,69 @@ semanticTokensTests = either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () - _ -> error "No tokens found" - , testCase "imported" $ do - let filePath = "./test/testdata/imported.hs" + _notokens -> error "No tokens found" + + , testCase "type family" $ do + let filePath = "./test/testdata/typefamily.hs" content <- liftIO $ Prelude.readFile filePath let expect = - [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 1}, name = "a"} - ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 4, startChar = 7, len = 1}, name = "+"} - ] - runSessionWithServerInDirAndGetSemantic "imported.hs" $ \res doc -> do + [SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 4, startChar = 13, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 4, startChar = 17, len = 1}, name = "a"} + ,SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 5, startChar = 3, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 7, len = 3}, name = "Int"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Int"} + ,SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 6, startChar = 3, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 7, len = 1}, name = "a"} + ,SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 6, startChar = 11, len = 6}, name = "String"}] + runSessionWithServerInDirAndGetSemantic "typefamily.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () - _ -> error "No tokens found" + _notokens -> error "No tokens found" + + -- ,testCase "module import test" $ do + -- let filePath1 = "./test/testdata/moduleA.hs" + -- let filePath2 = "./test/testdata/moduleA.hs" + -- content1 <- liftIO $ Prelude.readFile filePath1 + -- content2 <- liftIO $ Prelude.readFile filePath2 + + -- let file1 = "moduleA.hs" + -- let file2 = "moduleB.hs" + -- let expect = [] + -- Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProjectMulti [file1, file2]) $ do + -- doc1 <- openDoc file1 "haskell" + -- doc2 <- openDoc file2 "haskell" + -- check1 <- waitForAction "TypeCheck" doc1 + -- check2 <- waitForAction "TypeCheck" doc2 + -- case check2 of + -- Right (WaitForIdeRuleResult x) -> liftIO $ print $ "result of checking2: " <> show x + -- Left y -> error "TypeCheck2 failed" + + -- res2 <- Test.getSemanticTokens doc2 + -- case res2 ^? _L of + -- Just tokens -> do + -- either (error . show) + -- (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content2 tokens + -- return () + -- _ -> error "No tokens found" + -- liftIO $ 1 @?= 1 + ] + - , testCase "datatype" $ do +semanticTokensDataTypeTests = + testGroup + "get semantic Tokens" + [ testCase "datatype" $ do let filePath = "./test/testdata/datatype.hs" content <- liftIO $ Prelude.readFile filePath let expect = [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Foo"} ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 3, startChar = 12, len = 3}, name = "Foo"} ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 16, len = 3}, name = "Int"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 30, len = 2}, name = "Eq"}] + ,SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 3, startChar = 30, len = 2}, name = "Eq"}] runSessionWithServerInDirAndGetSemantic "datatype.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of @@ -270,38 +330,30 @@ semanticTokensTests = either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () - _ -> error "No tokens found" + _notokens -> error "No tokens found" + + -- record is part of datatype + , testCase "record" $ do + let filePath = "./test/testdata/record.hs" + content <- liftIO $ Prelude.readFile filePath + let expect = + [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} + ,SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 4, startChar = 18, len = 3}, name = "foo"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 25, len = 3}, name = "Int"}] + runSessionWithServerInDirAndGetSemantic "record.hs" $ \res doc -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found" + ] - , - testCase "module import test" $ do - let filePath1 = "./test/testdata/imported.hs" - let filePath2 = "./test/testdata/imported.hs" - content1 <- liftIO $ Prelude.readFile filePath1 - content2 <- liftIO $ Prelude.readFile filePath2 - - let file1 = "moduleA.hs" - let file2 = "moduleB.hs" - let expect = [] - Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProjectMulti [file1, file2]) $ do - doc1 <- openDoc file1 "haskell" - doc2 <- openDoc file2 "haskell" - check1 <- waitForAction "TypeCheck" doc1 - check2 <- waitForAction "TypeCheck" doc2 - -- case check2 of - -- Right (WaitForIdeRuleResult x) -> liftIO $ print $ "result of checking2: " <> show x - -- Left y -> error "TypeCheck2 failed" - -- res2 <- Test.getSemanticTokens doc2 - -- case res2 ^? _L of - -- Just tokens -> do - -- either (error . show) - -- (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content2 tokens - -- return () - -- _ -> error "No tokens found" - liftIO $ 1 @?= 1 - ] main :: IO () main = defaultTestRunner $ testGroup "Semantic tokens" - [semanticTokensTests, semanticTokensClassTests] + [semanticTokensClassTests, semanticTokensDataTypeTests, semanticTokensValuePatternTests, semanticTokensTests] diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/classImportedDeriving.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/classImportedDeriving.hs new file mode 100644 index 0000000000..b4b846a564 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/classImportedDeriving.hs @@ -0,0 +1,10 @@ +{-# LANGUAGE StandaloneDeriving #-} +module ClassImportedDeriving where +-- deriving method source span of Show occurrence +data Foo = Foo deriving (Show) + +-- standalone deriving method not in the same position +-- deriving instance Eq Foo + +-- a :: Foo -> Foo -> Bool +-- a = (==) diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/imported.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/imported.hs deleted file mode 100644 index 592b13f61b..0000000000 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/imported.hs +++ /dev/null @@ -1,5 +0,0 @@ -module Imported where - - -a = 1 + 1 - diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/typefamily.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/typefamily.hs new file mode 100644 index 0000000000..796c54858f --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/typefamily.hs @@ -0,0 +1,6 @@ +{-# LANGUAGE TypeFamilies #-} +module TypeFamily where + +type family Foo a where + Foo Int = Int + Foo a = String diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/valBind.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/valBind.hs index d70ea12fb2..a328baba5f 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/valBind.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/valBind.hs @@ -1,8 +1,8 @@ module ValBind where -hello :: String -hello = "Hello, Haskell!" +hello :: Char +hello = head "Hello, Haskell!" From 8fa303f6ef3d91a4c053b762e5fb89f99bb5ea56 Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 14 Dec 2023 07:43:22 +0800 Subject: [PATCH 16/73] add hlint config to ignore test data --- plugins/hls-semantic-tokens-plugin/.hlint.yaml | 1 + 1 file changed, 1 insertion(+) create mode 100644 plugins/hls-semantic-tokens-plugin/.hlint.yaml diff --git a/plugins/hls-semantic-tokens-plugin/.hlint.yaml b/plugins/hls-semantic-tokens-plugin/.hlint.yaml new file mode 100644 index 0000000000..072cf81614 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/.hlint.yaml @@ -0,0 +1 @@ +- ignore: { "within": 'test/testdata/*.hs' } From 813fa547cf5032943a2fcf01aca640281fd5f88d Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 14 Dec 2023 08:03:26 +0800 Subject: [PATCH 17/73] cean up test data --- .../hls-semantic-tokens-plugin/test/Main.hs | 106 +++++++++--------- .../test/testdata/{class.hs => TClass.hs} | 2 +- ...dDeriving.hs => TClassImportedDeriving.hs} | 2 +- .../testdata/{datatype.hs => TDatatype.hs} | 2 +- ...hodBind.hs => TInstanceClassMethodBind.hs} | 2 +- .../test/testdata/TInstanceClassMethodUse.hs | 5 + .../test/testdata/{moduleA.hs => TModuleA.hs} | 2 +- .../test/testdata/TModuleB.hs | 6 + .../test/testdata/TPatternMatch.hs | 6 + .../{patternbind.hs => TPatternbind.hs} | 2 +- .../{patternsyn.hs => TPatternsyn.hs} | 2 +- .../test/testdata/{record.hs => TRecord.hs} | 2 +- .../{typefamily.hs => TTypefamily.hs} | 2 +- .../test/testdata/TValBind.hs | 8 ++ .../test/testdata/instanceClassMethodUse.hs | 5 - .../test/testdata/moduleB.hs | 5 - .../test/testdata/patternMatch.hs | 6 - .../test/testdata/valBind.hs | 8 -- 18 files changed, 88 insertions(+), 85 deletions(-) rename plugins/hls-semantic-tokens-plugin/test/testdata/{class.hs => TClass.hs} (66%) rename plugins/hls-semantic-tokens-plugin/test/testdata/{classImportedDeriving.hs => TClassImportedDeriving.hs} (87%) rename plugins/hls-semantic-tokens-plugin/test/testdata/{datatype.hs => TDatatype.hs} (59%) rename plugins/hls-semantic-tokens-plugin/test/testdata/{instanceClassMethodBind.hs => TInstanceClassMethodBind.hs} (62%) create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.hs rename plugins/hls-semantic-tokens-plugin/test/testdata/{moduleA.hs => TModuleA.hs} (50%) create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TModuleB.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.hs rename plugins/hls-semantic-tokens-plugin/test/testdata/{patternbind.hs => TPatternbind.hs} (58%) rename plugins/hls-semantic-tokens-plugin/test/testdata/{patternsyn.hs => TPatternsyn.hs} (67%) rename plugins/hls-semantic-tokens-plugin/test/testdata/{record.hs => TRecord.hs} (62%) rename plugins/hls-semantic-tokens-plugin/test/testdata/{typefamily.hs => TTypefamily.hs} (77%) create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.hs delete mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodUse.hs delete mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/moduleB.hs delete mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/patternMatch.hs delete mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/valBind.hs diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 406d8b86fd..c1352f8722 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -21,6 +21,7 @@ import qualified Data.Maybe import qualified Data.Set as Set import Data.String (fromString) import Data.Text +import Development.IDE.Plugin.Test (WaitForIdeRuleResult (..)) import Ide.Plugin.Error (getNormalizedFilePathE) import Ide.Plugin.SemanticTokens import Ide.Plugin.SemanticTokens.Types @@ -95,7 +96,7 @@ semanticTokensImportedTests = testGroup "imported test" , SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 7, startChar = 13, len = 1}, name = "b"}] - runSessionWithServerInDirAndGetSemanticsFile "class.hs" $ \res doc content -> do + runSessionWithServerInDirAndGetSemanticsFile "TClass.hs" $ \res doc content -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do @@ -111,7 +112,7 @@ semanticTokensImportedTests = testGroup "imported test" semanticTokensClassTests :: TestTree semanticTokensClassTests = testGroup "type class" [ testCase "type class" $ do - let filePath = "./test/testdata/class.hs" + let filePath = "./test/testdata/TClass.hs" content <- liftIO $ Prelude.readFile filePath let expect = [SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 7, len = 3}, name = "Foo"} @@ -119,7 +120,7 @@ semanticTokensClassTests = testGroup "type class" ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 3, len = 3}, name = "foo"} ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 10, len = 1}, name = "a"} ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 15, len = 3}, name = "Int"}] - runSessionWithServerInDirAndGetSemantic "class.hs" $ \res doc -> do + runSessionWithServerInDirAndGetSemantic "TClass.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do @@ -128,7 +129,7 @@ semanticTokensClassTests = testGroup "type class" return () _notoken -> error "No tokens found" , testCase "imported class method InstanceClassMethodBind" $ do - let filePath = "./test/testdata/InstanceClassMethodBind.hs" + let filePath = "./test/testdata/TInstanceClassMethodBind.hs" content <- liftIO $ Prelude.readFile filePath let expect = [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} @@ -138,7 +139,7 @@ semanticTokensClassTests = testGroup "type class" ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Foo"} ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 6, startChar = 5, len = 4}, name = "(==)"} ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 6, startChar = 12, len = 9}, name = "undefined"}] - runSessionWithServerInDirAndGetSemantic "InstanceClassMethodBind.hs" $ \res doc -> do + runSessionWithServerInDirAndGetSemantic "TInstanceClassMethodBind.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do @@ -147,12 +148,12 @@ semanticTokensClassTests = testGroup "type class" return () _notokens -> error "No tokens found" , - testCase "imported class method InstanceClassMethodUse" $ do - let filePath = "./test/testdata/InstanceClassMethodUse.hs" + testCase "imported class method TInstanceClassMethodUse" $ do + let filePath = "./test/testdata/TInstanceClassMethodUse.hs" content <- liftIO $ Prelude.readFile filePath let expect = [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 2}, name = "go"} ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 4, startChar = 9, len = 4}, name = "(==)"}] - runSessionWithServerInDirAndGetSemantic "InstanceClassMethodUse.hs" $ \res doc -> do + runSessionWithServerInDirAndGetSemantic "TInstanceClassMethodUse.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do @@ -161,13 +162,13 @@ semanticTokensClassTests = testGroup "type class" _notokens -> error "No tokens found" , testCase "imported deriving" $ do - let filePath = "./test/testdata/classImportedDeriving.hs" + let filePath = "./test/testdata/TClassImportedDeriving.hs" content <- liftIO $ Prelude.readFile filePath let expect = [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} ,SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 26, len = 4}, name = "Show"}] - runSessionWithServerInDirAndGetSemantic "classImportedDeriving.hs" $ \res doc -> do + runSessionWithServerInDirAndGetSemantic "TClassImportedDeriving.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do @@ -183,14 +184,14 @@ semanticTokensValuePatternTests = "value and patterns " [ testCase "value bind" $ do - let filePath = "./test/testdata/valBind.hs" + let filePath = "./test/testdata/TValBind.hs" content <- liftIO $ Prelude.readFile filePath let expect = [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 5}, name = "hello"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 10, len = 4}, name = "Char"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 10, len = 3}, name = "Int"} ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 5}, name = "hello"} - ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 9, len = 4}, name = "head"}] - runSessionWithServerInDirAndGetSemantic "valBind.hs" $ \res doc -> do + ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 9, len = 6}, name = "length"}] + runSessionWithServerInDirAndGetSemantic "TValBind.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do @@ -199,11 +200,11 @@ semanticTokensValuePatternTests = return () _noTokens -> error "No tokens found" , testCase "pattern match" $ do - let filePath = "./test/testdata/patternMatch.hs" + let filePath = "./test/testdata/TPatternMatch.hs" content <- liftIO $ Prelude.readFile filePath let expect = [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 1}, name = "g"} ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 4, len = 7}, name = "Nothing"}] - runSessionWithServerInDirAndGetSemantic "patternMatch.hs" $ \res doc -> do + runSessionWithServerInDirAndGetSemantic "TPatternMatch.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do @@ -212,7 +213,7 @@ semanticTokensValuePatternTests = return () _noTokens -> error "No tokens found" , testCase "pattern bind" $ do - let filePath = "./test/testdata/patternBind.hs" + let filePath = "./test/testdata/TPatternBind.hs" content <- liftIO $ Prelude.readFile filePath let expect = @@ -224,7 +225,7 @@ semanticTokensValuePatternTests = ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 5, startChar = 9, len = 1}, name = "x"} ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 11, len = 1}, name = "+"} ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 5, startChar = 13, len = 1}, name = "y"}] - runSessionWithServerInDirAndGetSemantic "patternBind.hs" $ \res doc -> do + runSessionWithServerInDirAndGetSemantic "TPatternBind.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do @@ -249,11 +250,11 @@ semanticTokensTests = _notokens -> error "No tokens found" liftIO $ 1 @?= 1 , testCase "pattern syn" $ do - let filePath = "./test/testdata/patternsyn.hs" + let filePath = "./test/testdata/TPatternSyn.hs" content <- liftIO $ Prelude.readFile filePath let expect = [SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 5, startChar = 9, len = 3} , name = "Foo"}] - runSessionWithServerInDirAndGetSemantic "patternsyn.hs" $ \res doc -> do + runSessionWithServerInDirAndGetSemantic "TPatternSyn.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do @@ -263,7 +264,7 @@ semanticTokensTests = _notokens -> error "No tokens found" , testCase "type family" $ do - let filePath = "./test/testdata/typefamily.hs" + let filePath = "./test/testdata/TTypefamily.hs" content <- liftIO $ Prelude.readFile filePath let expect = [SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 4, startChar = 13, len = 3}, name = "Foo"} @@ -274,7 +275,7 @@ semanticTokensTests = ,SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 6, startChar = 3, len = 3}, name = "Foo"} ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 7, len = 1}, name = "a"} ,SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 6, startChar = 11, len = 6}, name = "String"}] - runSessionWithServerInDirAndGetSemantic "typefamily.hs" $ \res doc -> do + runSessionWithServerInDirAndGetSemantic "TTypefamily.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do @@ -283,32 +284,33 @@ semanticTokensTests = return () _notokens -> error "No tokens found" - -- ,testCase "module import test" $ do - -- let filePath1 = "./test/testdata/moduleA.hs" - -- let filePath2 = "./test/testdata/moduleA.hs" - -- content1 <- liftIO $ Prelude.readFile filePath1 - -- content2 <- liftIO $ Prelude.readFile filePath2 - - -- let file1 = "moduleA.hs" - -- let file2 = "moduleB.hs" - -- let expect = [] - -- Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProjectMulti [file1, file2]) $ do - -- doc1 <- openDoc file1 "haskell" - -- doc2 <- openDoc file2 "haskell" - -- check1 <- waitForAction "TypeCheck" doc1 - -- check2 <- waitForAction "TypeCheck" doc2 - -- case check2 of - -- Right (WaitForIdeRuleResult x) -> liftIO $ print $ "result of checking2: " <> show x - -- Left y -> error "TypeCheck2 failed" - - -- res2 <- Test.getSemanticTokens doc2 - -- case res2 ^? _L of - -- Just tokens -> do - -- either (error . show) - -- (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content2 tokens - -- return () - -- _ -> error "No tokens found" - -- liftIO $ 1 @?= 1 + ,testCase "module import test" $ do + let filePath1 = "./test/testdata/TModuleA.hs" + let filePath2 = "./test/testdata/TModuleB.hs" + content1 <- liftIO $ Prelude.readFile filePath1 + content2 <- liftIO $ Prelude.readFile filePath2 + + let file1 = "TModuleA.hs" + let file2 = "TModuleB.hs" + let expect = [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 2}, name = "go"} + ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 5, startChar = 6, len = 4}, name = "Game"}] + Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProjectMulti [file1, file2]) $ do + doc1 <- openDoc file1 "haskell" + doc2 <- openDoc file2 "haskell" + check1 <- waitForAction "TypeCheck" doc1 + check2 <- waitForAction "TypeCheck" doc2 + case check2 of + Right (WaitForIdeRuleResult x) -> liftIO $ print $ "result of checking2: " <> show x + Left y -> error "TypeCheck2 failed" + + res2 <- Test.getSemanticTokens doc2 + case res2 ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content2 tokens + return () + _ -> error "No tokens found" + liftIO $ 1 @?= 1 ] @@ -316,14 +318,14 @@ semanticTokensDataTypeTests = testGroup "get semantic Tokens" [ testCase "datatype" $ do - let filePath = "./test/testdata/datatype.hs" + let filePath = "./test/testdata/TDataType.hs" content <- liftIO $ Prelude.readFile filePath let expect = [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Foo"} ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 3, startChar = 12, len = 3}, name = "Foo"} ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 16, len = 3}, name = "Int"} ,SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 3, startChar = 30, len = 2}, name = "Eq"}] - runSessionWithServerInDirAndGetSemantic "datatype.hs" $ \res doc -> do + runSessionWithServerInDirAndGetSemantic "TDataType.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do @@ -334,14 +336,14 @@ semanticTokensDataTypeTests = -- record is part of datatype , testCase "record" $ do - let filePath = "./test/testdata/record.hs" + let filePath = "./test/testdata/TRecord.hs" content <- liftIO $ Prelude.readFile filePath let expect = [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} ,SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 4, startChar = 18, len = 3}, name = "foo"} ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 25, len = 3}, name = "Int"}] - runSessionWithServerInDirAndGetSemantic "record.hs" $ \res doc -> do + runSessionWithServerInDirAndGetSemantic "TRecord.hs" $ \res doc -> do -- content <- waitForAction "getFileContents" doc case res ^? _L of Just tokens -> do diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/class.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TClass.hs similarity index 66% rename from plugins/hls-semantic-tokens-plugin/test/testdata/class.hs rename to plugins/hls-semantic-tokens-plugin/test/testdata/TClass.hs index f83aae34cf..692754ec71 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/class.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TClass.hs @@ -1,4 +1,4 @@ -module Class where +module TClass where class Foo a where diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/classImportedDeriving.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.hs similarity index 87% rename from plugins/hls-semantic-tokens-plugin/test/testdata/classImportedDeriving.hs rename to plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.hs index b4b846a564..8afd8afbd9 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/classImportedDeriving.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.hs @@ -1,5 +1,5 @@ {-# LANGUAGE StandaloneDeriving #-} -module ClassImportedDeriving where +module TClassImportedDeriving where -- deriving method source span of Show occurrence data Foo = Foo deriving (Show) diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/datatype.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatype.hs similarity index 59% rename from plugins/hls-semantic-tokens-plugin/test/testdata/datatype.hs rename to plugins/hls-semantic-tokens-plugin/test/testdata/TDatatype.hs index 8ca0c1b2dd..894065e391 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/datatype.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatype.hs @@ -1,3 +1,3 @@ -module DataType where +module TDataType where data Foo = Foo Int deriving (Eq) diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodBind.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.hs similarity index 62% rename from plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodBind.hs rename to plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.hs index 19a422e0fc..68b634f470 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodBind.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.hs @@ -1,4 +1,4 @@ -module InstanceClassMethodBind where +module TInstanceClassMethodBind where data Foo = Foo Int diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.hs new file mode 100644 index 0000000000..24ea9efd28 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.hs @@ -0,0 +1,5 @@ +module TInstanceClassMethodUse where + + +go = (==) + diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/moduleA.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TModuleA.hs similarity index 50% rename from plugins/hls-semantic-tokens-plugin/test/testdata/moduleA.hs rename to plugins/hls-semantic-tokens-plugin/test/testdata/TModuleA.hs index 5dfe62e22c..7d2c2bb034 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/moduleA.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TModuleA.hs @@ -1,3 +1,3 @@ -module ModuleA where +module TModuleA where data Game = Game Int diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TModuleB.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TModuleB.hs new file mode 100644 index 0000000000..856160ec4a --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TModuleB.hs @@ -0,0 +1,6 @@ +module TModuleB where + +import TModuleA + +go = Game 1 + diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.hs new file mode 100644 index 0000000000..95e97c1abb --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.hs @@ -0,0 +1,6 @@ +module TPatternMatch where + + +g (Nothing, _) = 1 + + diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/patternbind.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.hs similarity index 58% rename from plugins/hls-semantic-tokens-plugin/test/testdata/patternbind.hs rename to plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.hs index c188b5c7b3..358d0a606a 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/patternbind.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.hs @@ -1,4 +1,4 @@ -module PatternBind where +module TPatternBind where (a, b) = (1, 2) diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/patternsyn.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternsyn.hs similarity index 67% rename from plugins/hls-semantic-tokens-plugin/test/testdata/patternsyn.hs rename to plugins/hls-semantic-tokens-plugin/test/testdata/TPatternsyn.hs index 3a3e0020b2..9590467307 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/patternsyn.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternsyn.hs @@ -1,5 +1,5 @@ {-# LANGUAGE PatternSynonyms #-} -module PatternSyn where +module TPatternSyn where pattern Foo = 1 diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/record.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.hs similarity index 62% rename from plugins/hls-semantic-tokens-plugin/test/testdata/record.hs rename to plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.hs index 9999a051f5..b3176a154f 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/record.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.hs @@ -1,4 +1,4 @@ -module Record where +module TRecord where data Foo = Foo { foo :: Int } diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/typefamily.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.hs similarity index 77% rename from plugins/hls-semantic-tokens-plugin/test/testdata/typefamily.hs rename to plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.hs index 796c54858f..d8c925e370 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/typefamily.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.hs @@ -1,5 +1,5 @@ {-# LANGUAGE TypeFamilies #-} -module TypeFamily where +module TTypefamily where type family Foo a where Foo Int = Int diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.hs new file mode 100644 index 0000000000..506af37a42 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.hs @@ -0,0 +1,8 @@ +module TValBind where + + +hello :: Int +hello = length "Hello, Haskell!" + + + diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodUse.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodUse.hs deleted file mode 100644 index acce9bf224..0000000000 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/instanceClassMethodUse.hs +++ /dev/null @@ -1,5 +0,0 @@ -module InstanceClassMethodUse where - - -go = (==) - diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/moduleB.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/moduleB.hs deleted file mode 100644 index 44037eae7c..0000000000 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/moduleB.hs +++ /dev/null @@ -1,5 +0,0 @@ -module ModuleA where - -import ModuleB - -go = Game 1 diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/patternMatch.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/patternMatch.hs deleted file mode 100644 index 25df18127e..0000000000 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/patternMatch.hs +++ /dev/null @@ -1,6 +0,0 @@ -module PatternMatch where - - -g (Nothing, _) = 1 - - diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/valBind.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/valBind.hs deleted file mode 100644 index a328baba5f..0000000000 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/valBind.hs +++ /dev/null @@ -1,8 +0,0 @@ -module ValBind where - - -hello :: Char -hello = head "Hello, Haskell!" - - - From 73a8f9b8eecd4423f9a913e05c583ca4c2c05e0b Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 14 Dec 2023 08:10:29 +0800 Subject: [PATCH 18/73] revert flake.nix --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 4e282cc2be..f0f92aa476 100644 --- a/flake.nix +++ b/flake.nix @@ -69,7 +69,7 @@ (pkgs.haskell.lib.justStaticExecutables (pkgs.haskell.lib.dontCheck pkgs.haskellPackages.opentelemetry-extra)) capstone # ormolu - stylish-haskell + # stylish-haskell pre-commit ] ++ lib.optionals (!stdenv.isDarwin) [ # tracy has a build problem on macos. From dc815dc6e59cbcacf7f7a4538d1da9a679dc7c70 Mon Sep 17 00:00:00 2001 From: Patrick Wales Date: Thu, 14 Dec 2023 08:16:41 +0800 Subject: [PATCH 19/73] Rename query.hs to Query.hs --- .../src/Ide/Plugin/SemanticTokens/{query.hs => Query.hs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/{query.hs => Query.hs} (100%) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs similarity index 100% rename from plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/query.hs rename to plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs From 7086d590b809c13133b86e9ef7512ff2340b0111 Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 14 Dec 2023 08:26:59 +0800 Subject: [PATCH 20/73] Build: add semantic tokens to lts21 --- stack-lts21.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/stack-lts21.yaml b/stack-lts21.yaml index 2920b0e807..b1d4d8632b 100644 --- a/stack-lts21.yaml +++ b/stack-lts21.yaml @@ -35,6 +35,7 @@ packages: - ./plugins/hls-splice-plugin - ./plugins/hls-stan-plugin - ./plugins/hls-stylish-haskell-plugin + - ./plugins/hls-semantic-tokens-plugin ghc-options: "$everything": -haddock From 8ed9790533de4b39af326761cf7f7b9eeeffee4b Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 14 Dec 2023 10:44:43 +0800 Subject: [PATCH 21/73] Refactor and add README --- plugins/hls-semantic-tokens-plugin/README.md | 28 +++ .../hls-semantic-tokens-plugin.cabal | 3 +- .../src/Ide/Plugin/SemanticTokens.hs | 2 +- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 84 ++------- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 163 ++++++++++++++++++ .../src/Ide/Plugin/SemanticTokens/Query.hs | 116 +++++-------- .../src/Ide/Plugin/SemanticTokens/Types.hs | 125 +------------- .../src/Ide/Plugin/SemanticTokens/Utils.hs | 64 +++++++ .../hls-semantic-tokens-plugin/test/Main.hs | 46 ++--- 9 files changed, 346 insertions(+), 285 deletions(-) create mode 100644 plugins/hls-semantic-tokens-plugin/README.md create mode 100644 plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs create mode 100644 plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs diff --git a/plugins/hls-semantic-tokens-plugin/README.md b/plugins/hls-semantic-tokens-plugin/README.md new file mode 100644 index 0000000000..fe3712876f --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/README.md @@ -0,0 +1,28 @@ +# Semantic tokens (LSP) plugin for Haskell language server + +## Purpose + +The purpose of this plugin is to provide semantic tokens for the Haskell language server, +according to the [LSP specification](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens) +It can be used to provide semantic highlighting for Haskell code in editors by given semantic type and modifiers for some tokens. +A lot of editors support semantic highlighting through LSP, for example vscode, vim, emacs, etc. + +## Features + +### Semantic types and modifiers + +The handles request for semantic tokens for the whole file. +It supports semantic types and but not yet modifiers from the LSP specification. + +Default semantic types defined in lsp diverge greatly from the ones used in ghc. +But default semantic types allows user with less configuration to get semantic highlighting. +That is why we use default semantic types for now. By mapping ghc semantic types to lsp semantic types. +The mapping is defined in `Mapping.hs` file. + +### Delta semantic tokens and range semantic tokens + +1. It is not yet support capabilities for delta semantic tokens, which might be +crucial for performance. + +2. range semantic tokens are not yet supported. + diff --git a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal index da1fd649c5..47f1d2bc9a 100644 --- a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal +++ b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal @@ -24,8 +24,10 @@ library exposed-modules: Ide.Plugin.SemanticTokens Ide.Plugin.SemanticTokens.Types + Ide.Plugin.SemanticTokens.Mappings other-modules: Ide.Plugin.SemanticTokens.Query + Ide.Plugin.SemanticTokens.Utils Ide.Plugin.SemanticTokens.Internal hs-source-dirs: src @@ -44,7 +46,6 @@ library , unordered-containers , transformers , bytestring - , syb default-language: Haskell2010 diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index 2c6a5ecbb2..9d327b03fd 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -1,4 +1,4 @@ -module Ide.Plugin.SemanticTokens (descriptor, Internal.recoverSemanticTokens) where +module Ide.Plugin.SemanticTokens (descriptor) where import Development.IDE import qualified Ide.Plugin.SemanticTokens.Internal as Internal diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 2d63b2bae0..1218815b9c 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -1,15 +1,10 @@ {-# LANGUAGE DataKinds #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} -{-# OPTIONS_GHC -Wno-orphans #-} -{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE TypeApplications #-} {-# LANGUAGE ViewPatterns #-} module Ide.Plugin.SemanticTokens.Internal where @@ -18,7 +13,8 @@ import Control.Lens ((^.)) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Trans.Maybe (MaybeT (..)) import Data.Data (Data) -import Data.Generics (everything, mkQ) +import Data.Generics (Typeable, everything, + mkQ) import Data.List (sortBy) import qualified Data.List as List import Data.Maybe (catMaybes, fromMaybe, @@ -31,10 +27,13 @@ import Development.IDE (Action, GetModIface (GetModIface), GhcSessionDeps (GhcSessionDeps, GhcSessionDeps_), HieAstResult (HAR, hieAst, hieModule, refMap), - IdeState, + IdeAction, IdeState, + Priority (..), TypeCheck (TypeCheck), - catchSrcErrors, realSpan, - useWithStaleFast) + catchSrcErrors, + ideLogger, logPriority, + realSpan, use, + useWithStaleFast, uses) import Development.IDE.Core.Compile (TcModuleResult (..), lookupName) import Development.IDE.Core.Rules (getSourceFileSource, @@ -60,7 +59,6 @@ import Data.ByteString (ByteString, unpack) import Data.Either (fromRight, rights) import Data.Either.Extra (lefts) import Data.Function (on) -import Data.Generics (Typeable) import qualified Data.HashSet as HashSet import Data.List.Extra (chunksOf, (!?)) import qualified Data.List.NonEmpty as NonEmpty @@ -70,9 +68,6 @@ import Data.Text (Text) import Data.Traversable (for) import Data.Typeable (cast) import Debug.Trace (trace) -import Development.IDE (IdeAction, IdeState, - Priority (..), ideLogger, - logPriority, use, uses) import Development.IDE.Core.PluginUtils (useWithStaleFastMT, useWithStaleMT, usesMT) import Development.IDE.Core.PositionMapping (zeroMapping) @@ -82,38 +77,15 @@ import Development.IDE.Spans.LocalBindings (getDefiningBindings, import Development.IDE.Types.Exports (ExportsMap (..), createExportsMapHieDb) import Development.IDE.Types.HscEnvEq (hscEnv) -import GHC.Conc (readTVar) + +import Ide.Plugin.SemanticTokens.Mappings logWith :: (MonadIO m) => IdeState -> String -> m () logWith st = liftIO . logPriority (ideLogger st) Info . T.pack - -- logWith :: (MonadIO m) => IdeState -> String -> m () -- logWith st = liftIO . print -bytestringString :: ByteString -> String -bytestringString = map (toEnum . fromEnum) . unpack - -tyThingSemantic :: TyThing -> SemanticTokenType -tyThingSemantic ty = case ty of - AnId vid - | isTyVar vid -> TTypeVariable - | isRecordSelector vid -> TRecField - | isClassOpId vid -> TClassMethod - -- | isLocalId vid -> TPatternBind - -- | isDFunId vid -> TClassMethod - | otherwise -> TValBind - AConLike con -> case con of - RealDataCon _ -> TDataCon - PatSynCon _ -> TPatternSyn - ATyCon tyCon - | isTypeSynonymTyCon tyCon -> TTypeSyn - | isTypeFamilyTyCon tyCon -> TTypeFamily - | isClassTyCon tyCon -> TClass - | isDataTyCon tyCon -> TTypeCon - | isPrimTyCon tyCon -> TTypeCon - | otherwise -> TNothing - ACoAxiom _ -> TNothing ----------------------- ---- the api @@ -140,12 +112,12 @@ computeSemanticTokens state nfp = ty <- lookupNameEnv km name return (name, tyThingSemantic ty) let localNameSemanticMap = toNameSemanticMap $ Map.filterWithKey (\k _ -> - either (const False) (flip Set.member nameSet) k) refMap + either (const False) (`Set.member` nameSet) k) refMap let combineMap = Map.unionWith (<>) localNameSemanticMap importedModuleNameSemanticMap -- print all names -- deriving method locate in the same position as the class name -- liftIO $ mapM_ (\ (name, tokenType) -> dbg ("debug semanticMap: " <> showClearName name <> ":" <> show tokenType )) $ Map.toList importedModuleNameSemanticMap - -- liftIO $ mapM_ (\ (span, name) -> dbg ("debug names: " <> showClearName name <> ":" <> printCompactRealSrc span ) ) names + -- liftIO $ mapM_ (\ (span, name) -> dbg ("debug names: " <> showClearName name <> ":" <> showCompactRealSrc span ) ) names let moduleAbsTks = extractSemanticTokensFromNames combineMap names case semanticTokenAbsoluteSemanticTokens moduleAbsTks of Right tokens -> do @@ -168,7 +140,7 @@ computeSemanticTokens state nfp = semanticTokensFull :: PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull semanticTokensFull state _ param = do -- let dbg = logWith state - nfp <- getNormalizedFilePathE (param ^. (L.textDocument . L.uri)) + nfp <- getNormalizedFilePathE (param ^. L.textDocument . L.uri) -- dbg $ "semanticTokensFull: " <> show nfp -- source :: ByteString <- lift $ getSourceFileSource nfp items <- liftIO @@ -182,29 +154,3 @@ semanticTokensFull state _ param = do pure $ InL items ------------------------ ----- recover tokens ------------------------ - --- | recoverSemanticTokens --- used for debug and test --- this function is used to recover the original tokens(with token in haskell token type zoon) --- from the lsp semantic tokens(with token in lsp token type zoon) -recoverSemanticTokens :: String -> SemanticTokens -> Either Text [SemanticTokenOriginal] -recoverSemanticTokens sourceCode (SemanticTokens _ xs) = fmap (fmap $ tokenOrigin sourceCode) $ dataActualToken xs - where - tokenOrigin :: [Char] -> ActualToken -> SemanticTokenOriginal - tokenOrigin sourceCode (line, startChar, len, tokenType, _) = - -- convert back to count from 1 - SemanticTokenOriginal tokenType (Loc (line+1) (startChar+1) len) name - where tLine = lines sourceCode !? fromIntegral line - name = maybe "no source" (take (fromIntegral len) . drop (fromIntegral startChar)) tLine - - - dataActualToken :: [UInt] -> Either Text [ActualToken] - dataActualToken xs = maybe decodeError (Right . fmap semanticTokenAbsoluteActualToken . absolutizeTokens) - $ mapM fromTuple (chunksOf 5 $ map fromIntegral xs) - where - decodeError = Left "recoverSemanticTokenRelative: wrong token data" - fromTuple [a, b, c, d, _] = Just $ SemanticTokenRelative a b c (fromInt $ fromIntegral d) [] - fromTuple _ = Nothing diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs new file mode 100644 index 0000000000..da0618fd8c --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -0,0 +1,163 @@ +{- | Mappings.hs +This module contains the mapping +1. our token type from(to) LSP default token type +2. from ghc type to our token type +3. from hieAst identifier detail to our token type +4. from lsp token to our token +-} +{-# LANGUAGE OverloadedStrings #-} + +module Ide.Plugin.SemanticTokens.Mappings where +import qualified Data.List as List +import Data.List.Extra (chunksOf, (!?)) +import qualified Data.Map as Map +import Data.Maybe (fromMaybe) +import qualified Data.Set as Set +import Data.Text (Text) +import Development.IDE.GHC.Compat +import Ide.Plugin.SemanticTokens.Types +import Language.LSP.Protocol.Types (LspEnum (knownValues), + SemanticTokenAbsolute (SemanticTokenAbsolute), + SemanticTokenRelative (SemanticTokenRelative), + SemanticTokenTypes (..), + SemanticTokens (SemanticTokens), + UInt, absolutizeTokens) + +{- | +1. from our token type to LSP default token type +-} + +semanticTokenAbsoluteActualToken :: SemanticTokenAbsolute -> ActualToken +semanticTokenAbsoluteActualToken (SemanticTokenAbsolute line startChar len tokenType tokenModifiers) = + (line, startChar, len, fromLspTokenType tokenType, 0) + +-- mapping from our token type to LSP default token type +toLspTokenType :: SemanticTokenType -> SemanticTokenTypes +toLspTokenType tk = case tk of + -- TVariable -> SemanticTokenTypes_Variable + -- left hand side of none pattern bind + TValBind -> SemanticTokenTypes_Function + -- any pattern bind + TPatternBind -> SemanticTokenTypes_Parameter + TClass -> SemanticTokenTypes_Class + TClassMethod -> SemanticTokenTypes_Method + TTypeVariable -> SemanticTokenTypes_TypeParameter + -- normal data type is a tagged union type look like enum type + -- and a record is a product type like struct + -- but we don't distinguish them yet + TTypeCon -> SemanticTokenTypes_Enum + TDataCon -> SemanticTokenTypes_EnumMember + TRecField -> SemanticTokenTypes_Property + -- pattern syn is like a limited version of macro of constructing a data type + TPatternSyn -> SemanticTokenTypes_Macro + -- saturated type + TTypeSyn -> SemanticTokenTypes_Type + -- not sure if this is correct choice + TTypeFamily -> SemanticTokenTypes_Interface + TNothing -> SemanticTokenTypes_Namespace + +lspTokenReverseMap :: Map.Map SemanticTokenTypes SemanticTokenType +lspTokenReverseMap = Map.fromList $ List.map (\x -> (toLspTokenType x, x)) $ enumFrom minBound + +fromLspTokenType :: SemanticTokenTypes -> SemanticTokenType +fromLspTokenType tk = fromMaybe TNothing $ Map.lookup tk lspTokenReverseMap + + +{- | +2. from ghc type (TyThing or name) to our token type +-} + +toTokenType :: Name -> SemanticTokenType +toTokenType locName = case occNameSpace $ occName locName of + x | isDataConNameSpace x -> TDataCon + x | isTvNameSpace x -> TTypeVariable + x | isTcClsNameSpace x -> TTypeCon -- Type constructors and classes in the same name space for now + x | isVarNameSpace x -> TValBind + _ -> TNothing + + +tyThingSemantic :: TyThing -> SemanticTokenType +tyThingSemantic ty = case ty of + AnId vid + | isTyVar vid -> TTypeVariable + | isRecordSelector vid -> TRecField + | isClassOpId vid -> TClassMethod + -- | isLocalId vid -> TPatternBind + -- | isDFunId vid -> TClassMethod + | otherwise -> TValBind + AConLike con -> case con of + RealDataCon _ -> TDataCon + PatSynCon _ -> TPatternSyn + ATyCon tyCon + | isTypeSynonymTyCon tyCon -> TTypeSyn + | isTypeFamilyTyCon tyCon -> TTypeFamily + | isClassTyCon tyCon -> TClass + | isDataTyCon tyCon -> TTypeCon + | isPrimTyCon tyCon -> TTypeCon + | otherwise -> TNothing + ACoAxiom _ -> TNothing + + +{- | +3. from hieAst identifier detail to our token type +-} + + +infoTokenType :: ContextInfo -> SemanticTokenType +infoTokenType x = case x of + Use -> TNothing + MatchBind -> TNothing + IEThing _ -> TNothing + TyDecl -> TNothing -- type signature + + ValBind RegularBind _ _ -> TValBind + ValBind InstanceBind _ _ -> TClassMethod + PatternBind {} -> TPatternBind + ClassTyDecl _ -> TClassMethod + TyVarBind _ _ -> TTypeVariable + RecField _ _ -> TRecField + -- data constructor, type constructor, type synonym, type family + Decl ClassDec _ -> TClass + Decl DataDec _ -> TTypeCon + Decl ConDec _ -> TDataCon + Decl SynDec _ -> TTypeSyn + Decl FamDec _ -> TTypeFamily + -- instance dec is class method + Decl InstDec _ -> TClassMethod + Decl PatSynDec _ -> TPatternSyn + + EvidenceVarUse -> TNothing + EvidenceVarBind {} -> TNothing + + +-------------------------------- +---- from lsp token to our token +-------------------------------- + +-- | recoverSemanticTokens +-- used for debug and test +-- this function is used to recover the original tokens(with token in haskell token type zoon) +-- from the lsp semantic tokens(with token in lsp token type zoon) +recoverSemanticTokens :: String -> SemanticTokens -> Either Text [SemanticTokenOriginal] +recoverSemanticTokens sourceCode (SemanticTokens _ xs) = fmap (tokenOrigin sourceCode) <$> dataActualToken xs + where + tokenOrigin :: [Char] -> ActualToken -> SemanticTokenOriginal + tokenOrigin sourceCode (line, startChar, len, tokenType, _) = + -- convert back to count from 1 + SemanticTokenOriginal tokenType (Loc (line+1) (startChar+1) len) name + where tLine = lines sourceCode !? fromIntegral line + name = maybe "no source" (take (fromIntegral len) . drop (fromIntegral startChar)) tLine + + + dataActualToken :: [UInt] -> Either Text [ActualToken] + dataActualToken xs = maybe decodeError (Right . fmap semanticTokenAbsoluteActualToken . absolutizeTokens) + $ mapM fromTuple (chunksOf 5 $ map fromIntegral xs) + where + decodeError = Left "recoverSemanticTokenRelative: wrong token data" + fromTuple [a, b, c, d, _] = Just $ SemanticTokenRelative a b c (fromInt $ fromIntegral d) [] + fromTuple _ = Nothing + +{- -} +-- legends :: SemanticTokensLegend +fromInt :: Int -> SemanticTokenTypes +fromInt i = Set.elemAt i knownValues diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 029c90df49..98f854fef1 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -13,8 +13,10 @@ import Control.Arrow (ArrowChoice ((|||)), import Control.Monad (forM) import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Char (isAlphaNum) +import Data.Either (rights) import Data.Function (on) import Data.Generics (everything) +import qualified Data.HashSet as HashSet import qualified Data.List as List import qualified Data.List.NonEmpty as NE import Data.Map (Map) @@ -26,13 +28,21 @@ import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as Text import qualified Data.Text.Lazy.Builder as Text +import Data.Tuple (swap) +import Debug.Trace import Development.IDE (Action, filePathToUri', rangeToRealSrcSpan, realSpan) import Development.IDE.GHC.Compat +import Development.IDE.GHC.Error (positionToRealSrcLoc) import Development.IDE.Spans.AtPoint (FOIReferences) +import Development.IDE.Spans.LocalBindings (Bindings) +import Development.IDE.Types.Exports (ExportsMap (getModuleExportsMap), + IdentInfo) +import Development.IDE.Types.Location (toNormalizedFilePath') import Development.IDE.Types.Shake (WithHieDb) import Generics.SYB (mkQ) +-- import HieDb.Types (DefRow (..)) import HieDb (DefRow (..), HieDbErr (AmbiguousUnitId, NameNotFound, NameUnhelpfulSpan, NoNameAtPoint, NotIndexed), ModuleInfo (modInfoSrcFile), @@ -40,32 +50,31 @@ import HieDb (DefRow (..), findOneDef, findReferences, type (:.) (..)) +import Ide.Plugin.SemanticTokens.Mappings import Ide.Plugin.SemanticTokens.Types import Language.LSP.Protocol.Types --- import HieDb.Types (DefRow (..)) -import Data.Either (rights) -import qualified Data.HashSet as HashSet -import Data.Tuple (swap) -import Debug.Trace -import Development.IDE.GHC.Error (positionToRealSrcLoc) -import Development.IDE.Spans.LocalBindings (Bindings) -import Development.IDE.Types.Exports (ExportsMap (getModuleExportsMap), - IdentInfo) -import Development.IDE.Types.Location (toNormalizedFilePath') - - +------------------------------------------------ +--- | extract semantic tokens from RenamedSource +------------------------------------------------ nameToCollect :: Name -> Set.Set Name -nameToCollect name = Set.singleton name +nameToCollect = Set.singleton nameGetter :: RenamedSource -> Set.Set Name nameGetter = everything Set.union (Set.empty `mkQ` nameToCollect) ------------------------------------------ ----- construct definition map from HieAST a ------------------------------------------ +-------------------------------------------- +--- | construct definition map from HieAST a +-------------------------------------------- + +-- this one might contain derived names +hieAstNameSet :: HieAST a -> Set.Set Name +hieAstNameSet ast = Set.fromList importedNames + where locatedNames = hieAstSpanNames ast + importedNames = [name | (_, name) <- locatedNames] + hieAstSpanNames :: HieAST a -> [(Span, Name)] hieAstSpanNames ast = if null (nodeChildren ast) then getIds ast else concatMap hieAstSpanNames (nodeChildren ast) @@ -77,10 +86,9 @@ hieAstSpanNames ast = if null (nodeChildren ast) then , not $ any isEvidenceBind $ identInfo d , not $ any isEvidenceUse $ identInfo d , Set.size (identInfo d) > 0 + -- some derived occName is visible (some fields) -- , not $ isDerivedOccName (occName c) ] - -- getNodeIds'' :: HieAST a -> Map.Map Identifier (IdentifierDetails a) - -- getNodeIds'' = maybeToList . getSourcedNodeInfo . sourcedNodeInfo getNodeIds' :: HieAST a -> Map.Map Identifier (IdentifierDetails a) getNodeIds' = Map.foldl' combineNodeIds Map.empty . Map.filterWithKey (\k _ -> k == SourceInfo) @@ -92,44 +100,16 @@ hieAstSpanNames ast = if null (nodeChildren ast) then ad `combineNodeIds` (NodeInfo _ _ bd) = Map.unionWith (<>) ad bd - - -infoTokenType :: ContextInfo -> SemanticTokenType -infoTokenType x = case x of - Use -> TNothing - MatchBind -> TNothing - IEThing _ -> TNothing - TyDecl -> TNothing -- type signature - - ValBind RegularBind _ _ -> TValBind - ValBind InstanceBind _ _ -> TClassMethod - PatternBind _ _ _ -> TPatternBind - ClassTyDecl _ -> TClassMethod - TyVarBind _ _ -> TTypeVariable - RecField _ _ -> TRecField - -- data constructor, type constructor, type synonym, type family - Decl ClassDec _ -> TClass - Decl DataDec _ -> TTypeCon - Decl ConDec _ -> TDataCon - Decl SynDec _ -> TTypeSyn - Decl FamDec _ -> TTypeFamily - -- instance dec is class method - Decl InstDec _ -> TClassMethod - Decl PatSynDec _ -> TPatternSyn - - EvidenceVarUse -> TNothing - EvidenceVarBind _ _ _ -> TNothing - +-------------------------------------------------- +--- | extract semantic tokens from NameSemanticMap +-------------------------------------------------- type NameSemanticMap = Map Name SemanticTokenType ------------------------------------ --- extract semantic tokens from ast ------------------------------------ toNameSemanticMap :: RefMap a -> NameSemanticMap toNameSemanticMap rm = Map.fromListWith (<>) [ - -- trace ("toNameSemanticMap" <> ":" <> showSDocUnsafe (ppr name) <> " : " <> printCompactRealSrc span <> ":" <> showIdentifierDetails detail <> " : " <> show tokenType) + -- trace ("toNameSemanticMap" <> ":" <> showSDocUnsafe (ppr name) <> " : " <> showCompactRealSrc span <> ":" <> showIdentifierDetails detail <> " : " <> show tokenType) (name, tokenType) | (Right name, details) <- Map.toList rm , not $ isDerivedOccName (occName name) @@ -144,7 +124,7 @@ toNameSemanticMap rm = Map.fromListWith (<>) Nothing -> Nothing semanticTokenAbsoluteSemanticTokens :: [SemanticTokenAbsolute] -> Either Text SemanticTokens -semanticTokenAbsoluteSemanticTokens xs = makeSemanticTokens defaultSemanticTokensLegend . List.sort $ xs +semanticTokenAbsoluteSemanticTokens = makeSemanticTokens defaultSemanticTokensLegend . List.sort extractSemanticTokensFromNames :: NameSemanticMap -> [(Span, Name)] -> [SemanticTokenAbsolute] extractSemanticTokensFromNames nsm = @@ -154,26 +134,20 @@ extractSemanticTokensFromNames nsm = mergeNameFromSamSpan :: [(Span, SemanticTokenType)] -> [(Span, SemanticTokenType)] mergeNameFromSamSpan xs = Map.toList $ Map.fromListWith (<>) xs + toAbsSemanticToken :: Span -> SemanticTokenType -> SemanticTokenAbsolute + toAbsSemanticToken loc tokenType = + let line = srcSpanStartLine loc - 1 + startChar = srcSpanStartCol loc - 1 + len = srcSpanEndCol loc - 1 - startChar + in SemanticTokenAbsolute (fromIntegral line) (fromIntegral startChar) + (fromIntegral len) (toLspTokenType tokenType) [SemanticTokenModifiers_Declaration] + + getSemantic :: Map Name SemanticTokenType -> (Span, Name) -> Maybe (Span, SemanticTokenType) + getSemantic nameMap (span, name) = do + -- let tkt = toTokenType name + -- let tokenType = maybe tkt (\x -> tkt <> x) $ Map.lookup name nameMap + let tokenType = Map.lookup name nameMap + fmap (span,) tokenType -toAbsSemanticToken :: Span -> SemanticTokenType -> SemanticTokenAbsolute -toAbsSemanticToken loc tokenType = - let line = srcSpanStartLine loc - 1 - startChar = srcSpanStartCol loc - 1 - len = srcSpanEndCol loc - 1 - startChar - in SemanticTokenAbsolute (fromIntegral line) (fromIntegral startChar) - (fromIntegral len) (toLspTokenType tokenType) [SemanticTokenModifiers_Declaration] - -getSemantic :: Map Name SemanticTokenType -> (Span, Name) -> Maybe (Span, SemanticTokenType) -getSemantic nameMap (span, name) = do - -- let tkt = toTokenType name - -- let tokenType = maybe tkt (\x -> tkt <> x) $ Map.lookup name nameMap - let tokenType = Map.lookup name nameMap - fmap (span,) tokenType - --- this one might contain derived names -hieAstNameSet :: HieAST a -> Set.Set Name -hieAstNameSet ast = Set.fromList importedNames - where locatedNames = hieAstSpanNames ast - importedNames = [name | (_, name) <- locatedNames] diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index dd93b033c1..4c08e09551 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -11,46 +11,14 @@ import qualified Data.Map as Map import Data.Maybe (fromMaybe) import qualified Data.Set as Set import Development.IDE.GHC.Compat -import GHC.Enum (boundedEnumFrom) +-- import GHC.Enum (boundedEnumFrom) import Language.LSP.Protocol.Types --- legends :: SemanticTokensLegend -fromInt :: Int -> SemanticTokenTypes -fromInt i = Set.elemAt i knownValues - --- mapping from our token type to LSP default token type -toLspTokenType :: SemanticTokenType -> SemanticTokenTypes -toLspTokenType tk = case tk of - -- TVariable -> SemanticTokenTypes_Variable - -- left hand side of none pattern bind - TValBind -> SemanticTokenTypes_Function - -- any pattern bind - TPatternBind -> SemanticTokenTypes_Parameter - TClass -> SemanticTokenTypes_Class - TClassMethod -> SemanticTokenTypes_Method - TTypeVariable -> SemanticTokenTypes_TypeParameter - -- normal data type is a tagged union type look like enum type - -- and a record is a product type like struct - -- but we don't distinguish them yet - TTypeCon -> SemanticTokenTypes_Enum - TDataCon -> SemanticTokenTypes_EnumMember - TRecField -> SemanticTokenTypes_Property - -- pattern syn is like a limited version of macro of constructing a data type - TPatternSyn -> SemanticTokenTypes_Macro - -- saturated type - TTypeSyn -> SemanticTokenTypes_Type - -- not sure if this is correct choice - TTypeFamily -> SemanticTokenTypes_Interface - TNothing -> SemanticTokenTypes_Namespace - -lspTokenReverseMap :: Map.Map SemanticTokenTypes SemanticTokenType -lspTokenReverseMap = Map.fromList $ List.map (\x -> (toLspTokenType x, x)) $ boundedEnumFrom minBound - -fromLspTokenType :: SemanticTokenTypes -> SemanticTokenType -fromLspTokenType tk = fromMaybe TNothing $ Map.lookup tk lspTokenReverseMap -- data SemanticTokenType = SClass | SVariable | STypeVar | SDataCon | SNothing | SFieldName deriving (Eq, Ord) --- !!!! order of declarations matters +-- !!!! order of declarations matters deriving enum and ord +-- since token may come from different source and we want to keep the most specific one +-- and we might want to merge them. data SemanticTokenType = TNothing -- unknown -- | TVariable -- fallback @@ -75,21 +43,8 @@ instance Monoid SemanticTokenType where mempty = TNothing -type SemanticCollect = (SemanticTokenType, LIdP GhcRn) - -showCollect :: SemanticCollect -> String -showCollect (tk, L _ x) = show tk <> " " <> showSDocUnsafe (ppr x) - type ActualToken = (UInt, UInt, UInt, SemanticTokenType, UInt) - -semanticTokenAbsoluteActualToken :: SemanticTokenAbsolute -> ActualToken -semanticTokenAbsoluteActualToken (SemanticTokenAbsolute line startChar len tokenType tokenModifiers) = - (line, startChar, len, fromLspTokenType tokenType, 0) -- { line: 2, startChar 5, length: 3, tokenType: SemanticTokenType, tokenModifiers: 3, string}, --- type SemanticToken = (SemanticTokenData, SemanticCollect) --- type SemanticTokenData = (UInt, UInt, UInt, SemanticTokenType, UInt) - -type SemanticTokenUInt = UInt data SemanticTokenOriginal = SemanticTokenOriginal { tokenType :: SemanticTokenType, @@ -106,75 +61,3 @@ data Loc = Loc deriving (Show, Eq, Ord) -instance Show (IdentifierDetails a) where - show x = show $ identInfo x - -deriving instance Show DeclType -deriving instance Show BindType -deriving instance Show RecFieldContext - -instance Show ContextInfo where - show x = case x of - Use -> "Use" - MatchBind -> "MatchBind" - IEThing _ -> "IEThing IEType" -- imported - TyDecl -> "TyDecl" - ValBind bt _ _ -> "ValBind of " <> show bt - PatternBind _ _ _ -> "PatternBind" - ClassTyDecl _ -> "ClassTyDecl" - Decl d _ -> "Decl of " <> show d - TyVarBind _ _ -> "TyVarBind" - RecField c _ -> "RecField of " <> show c - EvidenceVarBind _ _ _ -> "EvidenceVarBind" - EvidenceVarUse -> "EvidenceVarUse" - - - -printCompactRealSrc :: RealSrcSpan -> String -printCompactRealSrc x = show (srcSpanStartLine x) <> ":" <> show (srcSpanStartCol x) <> "-" <> show (srcSpanEndCol x) - - -toTokenType :: Name -> SemanticTokenType -toTokenType locName = case occNameSpace $ occName locName of - x | isDataConNameSpace x -> TDataCon - x | isTvNameSpace x -> TTypeVariable - x | isTcClsNameSpace x -> TTypeCon -- Type constructors and classes in the same name space for now - x | isVarNameSpace x -> TValBind - _ -> TNothing - - - - ------------------------ ------ identifier token map ------------------------ - --- type RefMap a = M.Map Identifier [(Span, IdentifierDetails a)] -showRefMap :: RefMap a -> String -showRefMap m = unlines - [ - showIdentifier idn ++ ":" - ++ "\n" ++ unlines [showSDocUnsafe (ppr span) ++ "\n" ++ showIdentifierDetails v | (span, v) <- spans] - | (idn, spans) <- Map.toList m] - -showIdentifierDetails :: IdentifierDetails a -> String -showIdentifierDetails x = show $ identInfo x - -showIdentifier :: Identifier -> String -showIdentifier (Left x) = showSDocUnsafe (ppr x) -showIdentifier (Right x) = nameStableString x - -showLocatedNames :: [LIdP GhcRn] -> String -showLocatedNames xs = unlines - [ showSDocUnsafe (ppr locName) ++ " " ++ show (getLoc locName) - | locName <- xs] - -showClearName :: Name -> String -showClearName name = occNameString (occName name) <> ":" <> showSDocUnsafe (ppr name) <> ":" <> showNameType name - -showNameType :: Name -> String -showNameType name - | isInternalName name = "InternalName" - | isExternalName name = "ExternalName" - | isSystemName name = "SystemName" - | isWiredInName name = "WiredInName" diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs new file mode 100644 index 0000000000..efe94772e6 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs @@ -0,0 +1,64 @@ +-- stand alone deriving +{-# LANGUAGE StandaloneDeriving #-} + +module Ide.Plugin.SemanticTokens.Utils where + +import Data.ByteString (ByteString) +import Data.ByteString.Char8 (unpack) +import qualified Data.Map as Map +import Development.IDE.GHC.Compat + +deriving instance Show DeclType +deriving instance Show BindType +deriving instance Show RecFieldContext + +instance Show ContextInfo where + show x = case x of + Use -> "Use" + MatchBind -> "MatchBind" + IEThing _ -> "IEThing IEType" -- imported + TyDecl -> "TyDecl" + ValBind bt _ _ -> "ValBind of " <> show bt + PatternBind {} -> "PatternBind" + ClassTyDecl _ -> "ClassTyDecl" + Decl d _ -> "Decl of " <> show d + TyVarBind _ _ -> "TyVarBind" + RecField c _ -> "RecField of " <> show c + EvidenceVarBind {} -> "EvidenceVarBind" + EvidenceVarUse -> "EvidenceVarUse" + +showCompactRealSrc :: RealSrcSpan -> String +showCompactRealSrc x = show (srcSpanStartLine x) <> ":" <> show (srcSpanStartCol x) <> "-" <> show (srcSpanEndCol x) + +-- type RefMap a = M.Map Identifier [(Span, IdentifierDetails a)] +showRefMap :: RefMap a -> String +showRefMap m = unlines + [ + showIdentifier idn ++ ":" + ++ "\n" ++ unlines [showSDocUnsafe (ppr span) ++ "\n" ++ showIdentifierDetails v | (span, v) <- spans] + | (idn, spans) <- Map.toList m] + +showIdentifierDetails :: IdentifierDetails a -> String +showIdentifierDetails x = show $ identInfo x + +showIdentifier :: Identifier -> String +showIdentifier (Left x) = showSDocUnsafe (ppr x) +showIdentifier (Right x) = nameStableString x + +showLocatedNames :: [LIdP GhcRn] -> String +showLocatedNames xs = unlines + [ showSDocUnsafe (ppr locName) ++ " " ++ show (getLoc locName) + | locName <- xs] + +showClearName :: Name -> String +showClearName name = occNameString (occName name) <> ":" <> showSDocUnsafe (ppr name) <> ":" <> showNameType name + +showNameType :: Name -> String +showNameType name + | isInternalName name = "InternalName" + | isExternalName name = "ExternalName" + | isSystemName name = "SystemName" + | isWiredInName name = "WiredInName" + +bytestringString :: ByteString -> String +bytestringString = map (toEnum . fromEnum) . unpack diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index c1352f8722..69426ebd1e 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -6,39 +6,41 @@ -import Control.Arrow (Arrow ((***)), (&&&), (+++)) -import Control.Lens hiding (use) -import Control.Monad (forM) +import Control.Arrow (Arrow ((***)), (&&&), + (+++)) +import Control.Lens hiding (use) +import Control.Monad (forM) import Data.Bifunctor -import qualified Data.ByteString as BS +import qualified Data.ByteString as BS import Data.Data import Data.Default -import Data.Functor (void) -import qualified Data.List as List -import Data.Map as Map -import Data.Maybe (fromJust) +import Data.Functor (void) +import qualified Data.List as List +import Data.Map as Map +import Data.Maybe (fromJust) import qualified Data.Maybe -import qualified Data.Set as Set -import Data.String (fromString) +import qualified Data.Set as Set +import Data.String (fromString) import Data.Text -import Development.IDE.Plugin.Test (WaitForIdeRuleResult (..)) -import Ide.Plugin.Error (getNormalizedFilePathE) +import Development.IDE.Plugin.Test (WaitForIdeRuleResult (..)) +import Ide.Plugin.Error (getNormalizedFilePathE) import Ide.Plugin.SemanticTokens +import Ide.Plugin.SemanticTokens.Mappings import Ide.Plugin.SemanticTokens.Types import Ide.Types -import qualified Language.LSP.Protocol.Lens as L -import Language.LSP.Protocol.Types (SemanticTokens (..), - SemanticTokensParams (..)) -import qualified Language.LSP.Test as Test +import qualified Language.LSP.Protocol.Lens as L +import Language.LSP.Protocol.Types (SemanticTokens (..), + SemanticTokensParams (..)) +import qualified Language.LSP.Test as Test import System.Environment.Blank import System.FilePath -import qualified Test.Hls (PluginTestDescriptor, - mkPluginTestDescriptor', - runSessionWithServerInTmpDir, - waitForAction) +import qualified Test.Hls (PluginTestDescriptor, + mkPluginTestDescriptor', + runSessionWithServerInTmpDir, + waitForAction) import Test.Hls -import qualified Test.Hls.FileSystem as FS -import Test.Hls.Util (withCanonicalTempDir) +import qualified Test.Hls.FileSystem as FS +import Test.Hls.Util (withCanonicalTempDir) testDataDir :: FilePath From 289fecdcc508efef4152191b669145c1d083eff9 Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 14 Dec 2023 11:36:33 +0800 Subject: [PATCH 22/73] Semantic token, filter names in Ast --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 24 ++++++++++++------- .../src/Ide/Plugin/SemanticTokens/Query.hs | 13 ++++------ 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 1218815b9c..804f519cef 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -91,6 +91,12 @@ logWith st = liftIO . logPriority (ideLogger st) Info . T.pack ---- the api ----------------------- +-- | computeSemanticTokens +-- collect from various source +-- global names and imported name from typechecked and hscEnv +-- local names from refMap +-- name locations from hieAst +-- visible names from renamedSource computeSemanticTokens :: IdeState -> NormalizedFilePath -> Action (Maybe SemanticTokens) computeSemanticTokens state nfp = runMaybeT $ do @@ -102,18 +108,20 @@ computeSemanticTokens state nfp = (hscEnv -> hsc, _) <- useWithStaleMT GhcSessionDeps nfp -- because the names get from ast might contain derived name let nameSet = nameGetter tmrRenamed - -- let nameSet = hieAstNameSet ast - let names = hieAstSpanNames ast - - -- ask hscEnv for none local types + -- only take names we cares about from ast + let names = hieAstSpanNames nameSet ast + -- ask hscEnv for none local types, some from typechecked can avoid asking hscEnv km <- liftIO $ foldrM (getType hsc) (tcg_type_env tmrTypechecked) nameSet - -- name from type typecheck - let importedModuleNameSemanticMap = Map.fromList $ flip mapMaybe (Set.toList nameSet) $ \name -> do + -- let km = tcg_type_env tmrTypechecked + -- global name map from type typecheck and hscEnv + let globalAndImportedModuleNameSemanticMap = Map.fromList $ flip mapMaybe (Set.toList nameSet) $ \name -> do ty <- lookupNameEnv km name return (name, tyThingSemantic ty) + -- local name map from refMap let localNameSemanticMap = toNameSemanticMap $ Map.filterWithKey (\k _ -> - either (const False) (`Set.member` nameSet) k) refMap - let combineMap = Map.unionWith (<>) localNameSemanticMap importedModuleNameSemanticMap + either (const False) (`Set.member` nameSet) k) + refMap + let combineMap = Map.unionWith (<>) localNameSemanticMap globalAndImportedModuleNameSemanticMap -- print all names -- deriving method locate in the same position as the class name -- liftIO $ mapM_ (\ (name, tokenType) -> dbg ("debug semanticMap: " <> showClearName name <> ":" <> show tokenType )) $ Map.toList importedModuleNameSemanticMap diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 98f854fef1..c10f66f1d1 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -69,19 +69,14 @@ nameGetter = everything Set.union (Set.empty `mkQ` nameToCollect) --- | construct definition map from HieAST a -------------------------------------------- --- this one might contain derived names -hieAstNameSet :: HieAST a -> Set.Set Name -hieAstNameSet ast = Set.fromList importedNames - where locatedNames = hieAstSpanNames ast - importedNames = [name | (_, name) <- locatedNames] - -hieAstSpanNames :: HieAST a -> [(Span, Name)] -hieAstSpanNames ast = if null (nodeChildren ast) then - getIds ast else concatMap hieAstSpanNames (nodeChildren ast) +hieAstSpanNames :: Set.Set Name -> HieAST a -> [(Span, Name)] +hieAstSpanNames nameSet ast = if null (nodeChildren ast) then + getIds ast else concatMap (hieAstSpanNames nameSet) (nodeChildren ast) where getIds :: HieAST a -> [(Span, Name)] getIds ast = [(nodeSpan ast, c) | (Right c, d) <- Map.toList $ getNodeIds' ast + , Set.member c nameSet -- at least get one info , not $ any isEvidenceBind $ identInfo d , not $ any isEvidenceUse $ identInfo d From 6cd8661a2410148abd7f60d90bf20d13593fa1aa Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 14 Dec 2023 17:45:43 +0800 Subject: [PATCH 23/73] CI: add consistancy check for wether semantic tokens computations is stable across different ghc versions --- .../hls-semantic-tokens-plugin/test/Main.hs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 69426ebd1e..8de1a9d9cf 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -244,13 +244,17 @@ semanticTokensTests = testGroup "other semantic Token test" [ - testCase "mixed parse test" $ do - -- let filePath = "./test/testdata/T1.hs" - runSessionWithServerInDirAndGetSemantic "T1.hs" $ \tokens _ -> do - case tokens ^? _L of - Just tokens -> liftIO $ 1 @?= 1 - _notokens -> error "No tokens found" - liftIO $ 1 @?= 1 + testCase "mixed constancy test" $ do + let filePath = "./test/testdata/T1.hs" + content <- liftIO $ Prelude.readFile filePath + let expect = [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 18, len = 3}, name = "Set"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 6, startChar = 23, len = 6}, name = "insert"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 6, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 9, startChar = 12, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 9, startChar = 18, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 25, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 11, startChar = 7, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 11, startChar = 11, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 12, startChar = 3, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 10, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 15, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 14, startChar = 10, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 14, startChar = 14, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 5, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 15, startChar = 9, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 15, startChar = 13, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 15, len = 1}, name = "+"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 6, len = 2}, name = "Dd"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 17, startChar = 11, len = 2}, name = "Dd"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 14, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 19, startChar = 9, len = 3}, name = "One"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 19, startChar = 15, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 21, startChar = 1, len = 3}, name = "ggg"},SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 21, startChar = 7, len = 3}, name = "One"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 6, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 23, startChar = 12, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 16, len = 11}, name = "Prelude.Int"},SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 24, startChar = 6, len = 4}, name = "Bar1"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 24, startChar = 13, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 25, startChar = 6, len = 4}, name = "Bar2"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 25, startChar = 13, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 27, startChar = 1, len = 2}, name = "bb"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 27, startChar = 8, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 12, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 18, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 23, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 28, startChar = 1, len = 2}, name = "bb"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 28, startChar = 4, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 28, startChar = 9, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 28, startChar = 13, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 29, startChar = 1, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 7, len = 4}, name = "cool"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 29, startChar = 15, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 22, len = 4}, name = "cool"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 30, startChar = 1, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 4, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 9, len = 1}, name = "c"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 30, startChar = 14, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 17, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 19, len = 1}, name = "c"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 31, startChar = 12, len = 2}, name = "xx"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 31, startChar = 16, len = 2}, name = "yy"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 32, startChar = 11, len = 2}, name = "dd"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 34, startChar = 2, len = 2}, name = "zz"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 34, startChar = 6, len = 2}, name = "kk"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 35, startChar = 1, len = 2}, name = "cc"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 7, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 15, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 20, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 28, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 36, startChar = 1, len = 2}, name = "cc"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 36, startChar = 4, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 36, startChar = 7, len = 2}, name = "gg"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 36, startChar = 11, len = 2}, name = "vv"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 37, startChar = 10, len = 2}, name = "gg"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 14, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 38, startChar = 18, len = 1}, name = "$"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 38, startChar = 20, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 24, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 14, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 39, startChar = 18, len = 1}, name = "$"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 39, startChar = 20, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 24, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 41, startChar = 1, len = 2}, name = "go"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 41, startChar = 6, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 42, startChar = 1, len = 3}, name = "add"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 42, startChar = 7, len = 11}, name = "(Prelude.+)"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 47, startChar = 1, len = 4}, name = "main"},SemanticTokenOriginal {tokenType = TNothing, loc = Loc {line = 47, startChar = 9, len = 2}, name = "IO"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 48, startChar = 1, len = 4}, name = "main"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 48, startChar = 8, len = 8}, name = "putStrLn"}] + runSessionWithServerInDirAndGetSemantic "T1.hs" $ \res doc -> do + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found" , testCase "pattern syn" $ do let filePath = "./test/testdata/TPatternSyn.hs" content <- liftIO $ Prelude.readFile filePath From 655d1e10a851862f3c60b1fe29bf08a0efef18c7 Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 14 Dec 2023 20:07:30 +0800 Subject: [PATCH 24/73] Update documentation, cleanup test, remove default modifiers --- plugins/hls-semantic-tokens-plugin/README.md | 32 ++++++++++++++++--- .../src/Ide/Plugin/SemanticTokens/Query.hs | 3 +- .../hls-semantic-tokens-plugin/test/Main.hs | 4 +-- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/README.md b/plugins/hls-semantic-tokens-plugin/README.md index fe3712876f..209658df29 100644 --- a/plugins/hls-semantic-tokens-plugin/README.md +++ b/plugins/hls-semantic-tokens-plugin/README.md @@ -19,10 +19,32 @@ But default semantic types allows user with less configuration to get semantic h That is why we use default semantic types for now. By mapping ghc semantic types to lsp semantic types. The mapping is defined in `Mapping.hs` file. -### Delta semantic tokens and range semantic tokens +### delta semantic tokens, range semantic tokens and refresh -1. It is not yet support capabilities for delta semantic tokens, which might be +It is not yet support capabilities for delta semantic tokens, which might be crucial for performance. - -2. range semantic tokens are not yet supported. - +It should be implemented in the future. + +## checkList + +* Supported PluginMethodHandler + * [x] [textDocument/semanticTokens/full](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#semanticTokens_fullRequest). + * [ ] [textDocument/semanticTokens/full/delta](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#semanticTokens_deltaRequest) + * [ ] [workspace/semanticTokens/refresh](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#semanticTokens_refreshRequest) + +* Supported semantic tokens: + * [x] class and class method + * [x] type family name + * [x] data constructor name (not distinguishing record and normal data) + * [x] type constructor name + * [x] record field name + * [x] type synonym + * [x] pattern synonym + * [x] pattern bindings + * [x] value bindings + * [x] imported name + +* Supported modifiers: + * [ ] declaration (as in class declearations and type family) + * [ ] definition (as in class instance declaration and type family instance) + * [ ] modification (as in rec field update) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index c10f66f1d1..3117969c16 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -135,7 +135,8 @@ extractSemanticTokensFromNames nsm = startChar = srcSpanStartCol loc - 1 len = srcSpanEndCol loc - 1 - startChar in SemanticTokenAbsolute (fromIntegral line) (fromIntegral startChar) - (fromIntegral len) (toLspTokenType tokenType) [SemanticTokenModifiers_Declaration] + (fromIntegral len) (toLspTokenType tokenType) [] + -- SemanticTokenModifiers_Declaration getSemantic :: Map Name SemanticTokenType -> (Span, Name) -> Maybe (Span, SemanticTokenType) getSemantic nameMap (span, name) = do diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 8de1a9d9cf..6554048550 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -306,8 +306,8 @@ semanticTokensTests = check1 <- waitForAction "TypeCheck" doc1 check2 <- waitForAction "TypeCheck" doc2 case check2 of - Right (WaitForIdeRuleResult x) -> liftIO $ print $ "result of checking2: " <> show x - Left y -> error "TypeCheck2 failed" + Right (WaitForIdeRuleResult x) -> return () + Left y -> error "TypeCheck2 failed" res2 <- Test.getSemanticTokens doc2 case res2 ^? _L of From a77eda2ca0a4d06273d1b92a0d81ec206588618e Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 15 Dec 2023 00:19:13 +0800 Subject: [PATCH 25/73] Fix: IO now classfied to TTypcon, add test for GADT and data family, Update documentation --- plugins/hls-semantic-tokens-plugin/README.md | 11 ++- .../SemanticHighlightingSample0.png | Bin 0 -> 320582 bytes .../SemanticHighlightingSample1.png | Bin 0 -> 320582 bytes .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 10 ++- .../src/Ide/Plugin/SemanticTokens/Query.hs | 3 +- .../hls-semantic-tokens-plugin/test/Main.hs | 74 +++++++++++++++++- .../test/testdata/TDatafamily.hs | 11 +++ .../test/testdata/TDatatypeImported.hs | 6 ++ .../test/testdata/TGADT.hs | 7 ++ .../test/testdata/TModuleB.hs | 1 - 10 files changed, 111 insertions(+), 12 deletions(-) create mode 100644 plugins/hls-semantic-tokens-plugin/SemanticHighlightingSample0.png create mode 100644 plugins/hls-semantic-tokens-plugin/SemanticHighlightingSample1.png create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TDatafamily.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.hs diff --git a/plugins/hls-semantic-tokens-plugin/README.md b/plugins/hls-semantic-tokens-plugin/README.md index 209658df29..e39c44c55b 100644 --- a/plugins/hls-semantic-tokens-plugin/README.md +++ b/plugins/hls-semantic-tokens-plugin/README.md @@ -40,11 +40,16 @@ It should be implemented in the future. * [x] record field name * [x] type synonym * [x] pattern synonym - * [x] pattern bindings - * [x] value bindings + * [x] pattern bindings (map to parameters in standard token) + * [x] value bindings (map to functions in standard token) * [x] imported name * Supported modifiers: - * [ ] declaration (as in class declearations and type family) + * [ ] declaration (as in class declarations and type family) * [ ] definition (as in class instance declaration and type family instance) * [ ] modification (as in rec field update) + +## Semantic highlighting sample + +![semantic highlighting sample](SemanticHighlightingSample0.png) +![semantic highlighting sample](SemanticHighlightingSample1.png) diff --git a/plugins/hls-semantic-tokens-plugin/SemanticHighlightingSample0.png b/plugins/hls-semantic-tokens-plugin/SemanticHighlightingSample0.png new file mode 100644 index 0000000000000000000000000000000000000000..33cdb171efe1235d89f092daa69b7c0be4907ef0 GIT binary patch literal 320582 zcmZU)1z21$w>CP14enNol@=-Pt}Rkr1}pCF?hID6Kud9VC{Wzp-QBIYy9^AMp7Wjm zKlk1}PbS$(Rx&%utgQ9E+2KkGQW&Ttr~m)}L;8!j3IKpq001CNAtSx!taPfK0|4lv zmY+W>Nq_!KspMpDW@%#z0DK9L*8pjP2MIH@f5ygQQ$irWCQ>O?;J=_`BgX0>ynTm) zEfW=xWh6yJpwk=%!O$g8K87sd2t*p4MIsWI>M}0G^L)%YYP)QGXluM$_aA%O8)@XY zTLVJ;(rADD%}D|L4M)p!%nlft{8%hE1r|j-6vfj7^2cPd820yw!)YQdwI{mI1F7uG zd53CYi!b_-38G2kAV7EkuWs!4c#ww(z=KPliU|Or+hd|3`tp}pJ?ni4@KcLuG)H`c zR`hhdM|~H~yJ&bv27r|+xnc?mq>p-l1MMMM>2ofTk$I))2PDW?@B#~qeGXuYGt4&o zJAzT8FvBw_eWcy80L$vqBRNQuk4Qop4R=S@$rJoC?(%f@Gm+u#xgiDvmn;b(Z6?Ra z@@QH(j+0zSgH#$h0Y)Wv4}~~fh)@q5wJc#mQH|91`v=k_jx11RHG~KsSc%i~Zfm9J z3qfYaJJiij=o-lU5~>~Y5KW3L#IC0ekN8ofgC5>^mYR2;hYU;Zs|Aa@%2(!nzba5^ z=fCjF4yYX%lSq6;8N>!t)-mE!ONf;ME5UvWKEXp~Cd!1gRvnV`k#YU@D2{1Lq$EW% z{iqyy?;Po&Q!P&c4@PgR#8>DD*0f{eu?t49=7#n@ZiPB`GNbe!_W;QZVfzCCjmW`b zokNYrpbZ zIc`ZTwwx223f`+=qwrE9vj(8gxv=7xz$<$z8e_G9Z-W8kv4L`w2+{Or2I5sYQt0Iu zS;$W)Kd}E|MWd-+ql&%z+m2Z^RPug6>6(yU@-KuIdoKVv7VXS5LU~KfUV4MgMEvD^ zEGQmpdo)CC?d9wNcq`ETO)f9u2Zo2q6o@grvxm}%m@U{M^zD{k0~R?-Ik2K>gK@>$ zfPDP~MG=`SI5gNgSfQ&7CCBJMQ8^keT}ebdKJuEvIY9ffyt^RwUC5!*oB3{xfcK|J z&lQgT`-@~%q-radrL6u_uw`^V4-$HTDvmeQSnoiww*KVBhUJ^WMO5QAF%QapDZ)wi zV+P~nFg_6zeZX>9CQ~Kig77iA8=OEWSsw`yPv9Guq3r?=AGwZh3_*!?hjtoO25Hgz z$I$iwT843xHs^Hb`YR$+{Q!rECo&9PJZf-$&PXX7&09a5V7Wg@XPi+7TgO|dkSA!4 zk8f+Znhy_lYu(w(iU{CbXl{Moe_w#e4;>my^sSiS*!66P)hR|zR0P%R6$6qMS* zU4eIu_o*X?QF%-M=g}Mr-SmKrv^aF2zFAu&kL{PNp$NT^-{^#N%OsgoZ+-_yU zA<6d>2ri^4;R>H3Cp`C!YT(Mm+_NzDQLf>+KmsvqcIFhA(4aE43KQ^v>6#k$2t|6q=5`a&@?Gb=Z%GxKRiWwytv*J{UV zc{cjDuDbW{+vHN|Ix6qD+(EcY)l1waBtDyRmF5=RFl+|Ta^(~XqE8&oI6=+39M1o z0@|{bCAj@`%e)l}%HGWDoW-^nnJSvHm}n@y_^D8!J-K*&aZTmL=S%TS)hZQNYFRco z`PXu6rgTcM$n$fxVq`YgCk8DBMe84PZe`9|<2BMtb4#5|a3`KisWpmLB1Q)2Xh@-* z<|C#yW@$z)2iUGQE)D#P{HFW@u1l_7uC=bKE^#h}`|1belP5FX*QGZNUX5O|*SCkd z^H9zn4`lB!hk>qlH=miYJj+E?MMU+B^nbU@zNElt;S0nn#G*UdgCv8zzcpmH8kTOF zbh~~lMECx7h<@{Ch(e3siLlY#t}KQy2;YjEWQmn&{80Tr`HS93$KGnP0@vOc%&0X1Ru5a+aIlqqen(=n=*77RwRQFyp zIH)kvjMOx5Mb-<@t8T&c_PxG6XFgv$dQObVRg4~Y%QSy)D~T#)NB%G{zmmAuJXt>- zbC9@O-&sDrK9aeozx~46K_!Y^LA~*DIXZoGt<&$#J}DiBAohL6_Goe^A9*lh2vR&! zEyx-~i==_Hj>U#Gh+d3#fZ>XjPUs~Vg-wW4$9Rl1pESHT=j7cj)NLMIFRAo?>plH@ za>*qrnfIwu*|}X(WyG#LL-aDFv&0xVjgt&HXT1wOauIy^vQgi|%OZSnd8q8XR+2p= zpMvjCF3xb_@De_(NY}{!qOSWe8P`WMOVg@|DWjLM!sm6r|0b6ukGLRAB}GNLSh`qS z1=@!Y^OdW>x)i$bvvR(FF)Ee)bj73lqH#M$E=zGkej!ovi>$(O#%&1EXvyYHzVmd+ zwD$uk%nF7k1~JIb(%xs#VfIPcOsy2${#sza8!>Xq_)>aVN{%C?`q3+%ETL5x>Z{-~ zHkSrl`MaX)Y2|6@3F=+iB6U0xj|&IWxP2l0K>c$}dM!>}{xDwhOa0&X58Icis4g2Z zKB*(>w~@zU+4_cRo1uf!ad+~QDO4F#+_Qd%UB!x#pCbHHzbS4jS|{6a`;J|MI2Jg# z$HdJF#%+>K*{s(}|14%#*N*1rE#{@$)A>wt3T!!ZyVfqCYWXJ!iT0|+6vjY zH5`VW{-BonD78m+BBJh6de-pQV?2BWFGh=|W`&RM@$c;1!rUzwr*JF-lh5}O9Xe83 z-DZxJc_r+AE_md#jTnQ5VS8!|@^gQA7HyXD?v@yjluvA7=5miZ=Ji^9xeR9z(@@ag z(Q9>JI@IdF={e%Dk7}Lt1Up{!MAx&#Rt<%=FH=m?x8OFUc$Sc^Js&S<3iN_R?lCb zr*3y(?Qk%wBeK=y8=1%7FE=?nNcMGYuHMU2MAPmsx@*njXR;#1J`1-$ZZ1X&|0*hq zD8fEHs$9&TT3^+N*}Frp9--8N9~CbcAe|9rCV+D<20#ryfcV@;@=dPb%SY54!D@Q! zdqqu8NmYC-z~SbLw++@)b0e_hboBfl@#er77?ur;-9S{*nD69wPzR?m?1UhX1K%38 z7wqK+`GbF%k5O#F8WEk;c+Q=nEO4qmW|!76r>`zWgsG;qnY=uJ{xyvZKmd{e5MNWk z*B}gh`+w4sKso^Yf91mg0HKxug#Xl0c#Z#Ev9IBu%zwx5KfeJ$uXnhw!6OUqe`+HY zWWoPW8e!_S3?K&nEG_*SgN>a`P3@d5>|NFz2vS}%P#nH|bp`+kss9C_w91E9w+Faj zsjBIsDKE!wY;VhKXku?<%Isn5@UI_)kkZ4}#?G1FLx}3XYVg0N|7EjK zQT|sI7i%FZO?f5C&-PBHl-$g$%&b(xsFakHf=(u8{3_y-{}F$^6QZ(kadF^hVR3hN zXLjdcws$gTVdLZDV_{`yVP|K0t-<8%Y3E|-!DQ$B{=XafpLWDeosFF=9b7E!?I{1X zYiMNe>LNr%^{=DD*FG5syUlFeYUrK?bJp1 z|4!I{g#UNpe*^_t{yqEu8jJrP=>N)n9cf`yL6-kBYr?3-4=}^mi6pfYS5$qCUzgdx z3(oTOi|)VU*EF1+YJ9Oa2>>7pkQNtH^#C6GB6g7V&fbPMH#h%?rF48R{v`|-dFj@A zSOZaO=~Dm}sziirz^7Fxm@ybTsDS~;=mWO9J5iT^#-jIahQQgW_f4&d!sXg(dU|rY zP_uwj#`_Sh+Qvs~Csj(`iDpIp-%4$8Cu`NIyW^?aluJI9JXPzi@I^Ms>@~*yZdx@K zh6&4LycWpubb4vrHY=hG3H}K?K4P+T8h=RR>D5xk()i@1qDjM-p8}u8Fq=0e>2->* z$#@+gW}_)McY4-p44=k@m40Y(F-cO(;w>ha3aD(=W>EbsirRgEaW z+bM9a@x9YedFYS=!dc-}` z;)w=^+a_P@Wc_)uD(s&DEyo|?G-0p)&`;ljFoX0#F`!y21RvxnN#SYQ+!=c(m zRr6Ied^CI_F398f`wzmFzG@w~~v^PF~%4GqUUzW=UTTu*xTw#*O>G|IC+}x&_ixD&Zg8hlxQxx;#N z$>!N(QZFSIx-Kb1Rw{67IPR60i&5bscYWQbu$W)@z!gk_>BkN{rd~!>mFmQsR!r15jH=WUv$zj zA~$fGdZPZY?V}pA8<}eTDwB_1x4rYDu$4s)i@)CX$-YnfUWE#<;0_k~R04)yof{G8lmAKlp2hI)_ogCGm#7f9 zv)ifM7p06y`DPOq>T#2!Ii{~2pImNv)?BJLM-y8XH0xZX1NNqG;6c@_O@isdzA3^a zLiDH4$o{BH0@`&pUr9I@-g?}gu6~{_lpnp-gWfSpjEiXgXdmCO(RR-$G+bYoA{s$p z=T-b&ocX=n^bhnPqWZcuv@0a6c+UT0qV-}GL2dJvOn2aT0#iOqTbP1@a|FwYfJ+{Y zd`ilpVj8d5cAwubaH34f8bf*05|M#F>!lEfv_i=zjRl7+$?qgHM@zzH{+&;rHKxO6 z{crjMbhbNcTcOwx)QN^lq;q{)Z01p)ozMpb7htn5HJX4M`WOF3jX?##puYURHjv1 zyvNl#QYKmi*MZ@@{Pg9K?*lvS=&<*|_?eg9CH3)AzHDOlyU(G?(vvO1Q=l?lXFCAcLix^4cOSG4WhN7J3Cr-il~}5`7x{@- zkOn9K{MB)`)t~;RNE4MEP7(9n5(`2B5Cvh8XBGNAD~<5+d8Bk^Oy$d7Js;ileY09+gHq&?06&{mJ*kuAD^-~cZ zOcC=|K8_|j5e%#2W!MveZk@>qxL4u6yc%YknbQDV53?YxfrXl{Xid<=hV;yQjH_;qeBBG?iouCZcl(y7SnBMHUz=ykb$>~| z6q8)_8iN2i#mKeNw*!nr^tv8b>UYm-^ zWg?5WIyMgiZpY%^NqE@mxlAv6Y|23Fu4fYW62CRt119wXZ@Fu7F5h=*!QoX$N-HH( z+?RGv#Lz}@B+x!&>;Az=L;@jUz3D^SW0#D*SZlTqU8^Bq$Es4BC0E}782&()9ZIn= zgfCOX{^gXF7MEG{DFhY4zzEBmlD8)?PMj|JK~c%KScju1L$1)(xMZWLw3q<_3jjD0 zz2&p0jiw-C82RCZ=%`(y&P1>AM^IbFHWyr|u)D1N#Y`cE!j7uF?dTzZ$L;O;$)*7O zXoe6+pVs8qQ8XEd6vQlsc`vH^ckS62qVNU05!Zf}e_@~Tx?I)j|DBH77TDpD{+AZtp4SFm$G!-zdnMaWv;voCSo@W*4|5DPH(qgmKMfl z1r#UhdqmWQYNov$&W|e*+#)#L9)I;ZZ)=v1M*j` zs*Aa+@}O1wwYuD##krQxS+m(0daKh^)htI0+OR{js~6=cP~?EaOs|)SOmOLq*GSHb z6zd4XBn^Bs(d4Q>HAUS?RyWuog7}c$dR#T<;<@a$tG>mAukT%odvmBW_1D+?7{%)p zOWC2wZj_1>b~ZcO$QL<2u^L0h2~B=%^!@V2YrlU--W5yGJ&9(FKfd_4uFyz;uy46> zy%qWS9nAaep5`W+&jg>EGEKNIMIWzEUodc}#jj4c$u0L0T`q9Vcdy-ZmDK%NHVVGt z0~{~;xE46(>~&KztM^?t;#I5F;yRoz%#?mz>vVTG+eTwVH9@bY2jT%N%K%ZLSUDsMDAIafzK)VftxW@%6v+uI|rN2mZ=SCq|aMgC~d*gdoeLX@iKfGB0%Z6QtE? zOSUo}5D57Q_=0ui8^B39_xon&I*8(VCNGBMfqOKA?|T(|0RXnbyXDr@MIK1wDU~d!Flo6{$LAF!d5! zuAky)+MnUEaSig23feBp1d4t8nQ4xuZ7}LH%eDfGyvMB%3R2Hl3rSfCda`GxNm=9h z&!*^6{*rb`;-6fpF`JoFY9(w98M6xN`UujC2FSaY5j|! zU9&)eM%J#JCco!L)pjxX_b!_4cIg-S(Sr_exTS>#PSv?>G%3I>lHXGb&~X!4kprTI z8m$T0;%t%KCMQpq8iPjVc~ma8sEZu8`*yX46PS!6Aw?p}ug+%1XtD?agF5?CvlL$j z&Wgqcd8OzWmJ4F@y;bL*Hql(k?|IIpC|T0|5h%EL`GVhoL8V!4rw>EN^>*q}ZT{Y0 z+fNhZBXEqg0}P5?pG=PPHoC(LEq1#~|5z&R_OCNa$-l+Vw^Bo$o=exCG-7_~qJ(H^3P3)TMf42Z|gG(_XzCsqLFl7 z#$W2w_@0)V`z?2fDxZhdY$K*$R`xG$J&|zSPte-ev%BqB*=EO_GOsp6DF&$7n`~!_ zDzW?yA`Cy)+AON1<5y?ey&T`YL%9_x)6zBLEXu=q-J>oL6Ox`R`G@gfJDE(=(q`>O z`7$#_hVMTu8ya3vH%l`1nZZU=xHZ_S&%V3lYI4|MHmYS74ZskPJCAYH$rk^EvdOb6Lg4Wf zD2ooE1YsWUJ&YeRqP^8T6Q%P8K5aaFE=fpCzHk~%nH3~F=DYz62z2{iSLH`mYbGt8 zDGFtP#6GubP8!u@mZSJB8Q1S(5!xh0LfRdSBP%y1IzQLAo$Lims1Q>_eGj~zs`z-@ zj*bR}Pj7a$?Kna&0LVgZF=SUbS5(#_m`d<>xJXW$jfOt`?t|$lWPCmYJ=;C*8DyKO zog6)%$b7p!-IN2}vHqwB8R=8)$~TAQgzK{G{t~+ici9su)|s}`u6nYZE9DzcW|(q> zWlYo?wSD9B|Ek9VVbQM`#vQq_#bw29>EL=-@#dxzIuo!u#BEkz6~==|i7WVZ^uk{K zf+zPQ_y|qsEo2u(Z`#JF9k#p}F-X%OWWCx{E1lr!?-H?EScaczy^Dlp(G9~_eADjLg5LTtsOdF1{qr&T27sq;jXt2k*hFb{Gy)W1ZHeE0 z7AS7{EbJyQhye2y()mZ{1X!xIFUzFa=)fKLb2KqRFvj@E9&x|ZgMJJBo-Tc!B8lO- z>)dAlU0PrVD%FSp4(NchU(0`|w-5|h5V(RoREzD50>PjoXAKd=623(6rYo|YqrqDB zkMZmiX+ASdFyvx4?uKt~81~>sfiIwp@jKt7jgr7Sgx?hE)B;)OP8TX(w2Xi&6bO=< zuDsnRcTWCrx`KdUkOvbim6n`)FUZdcE$MLD9)Lma6=&JC4tqk1*&7Z@`TXAQ0m=1f zayZSoLzOa8MZu**b*NOfl@ftHOgm5_Lw+9}TcZmpxhEZI?6X1Es@)ed0HW}-%Ma2= zZ5RG|EK(DMQa`FQ102Rd~bNw*Pb zHjDJ}5XjFxDS9Z7t!d?-7Ay{q*gY%PkCxWVIwK^2r5|k$*I&E>DGAdBIxyrl(>hY< z_sM@?`JDnqyW|t(8+$2`c|kfN18qM0Y{AIRdtx zg#N!(*qfKj_5FX31P*xE4biL7$#JaRZaMm}U*QYgKW#E%yqRgnS2M}7oK0jb)yG3~ zcs`G=av_2j=LYsPm41BBF4uat@EL^Z4Z$nTHK2~CYkiu}y*^W}gzFAH`)vToft>b3 ztk$0%fR9GTj7dR9z}QEat?5Jh_tr;0q+C=2n%^pw6_&J1CB+_ z)~kZxJz%lh_Ps_{s?Cx(kM|U7CKj+Nj+uuD+^>JWk8dY<-%dUI${6G3Fy=|oQG;`qo)?~gP0|GKQEsaL1XU;GVi ztcZr?*gi!uJ}*LiAD&?yIiRaIA6b$qm4dkxVdclX0qq*fX4Z7tZk`L?qx<9O%7075qGuv%B_Oo=ph!Rei#u;9bCW0fT+|Y2EgOL3 z&{gnowcA(9!Yb8n*~3Kh*2D2;mTZJ4upuDqN5mel@uRB9X*yX91Kv@jE@@mgH}tmt z@6EGZ#Jo@=o%7yeyo48MyhmSn&Ag2*wwGA(YI0*i!7LFQ0lr`mG%cTU7acv49nu ze0$9_v9_+-yGw-7DMvAgVUUgDd-Pz?MXUE+22w)=9}XX2L(p@I_Ul{(ac(H))e4L4 zV$~u<<#r*omZ8Nn%irxU?*?yd3}469D6w?1Kl%j9iTZyzQ`vdRktHw|bg9s6sC+ z?o)j{4=SzFO+$#);K)%rgnaD&6ZQP+{}`9bl~QRMieAYhqZL)}jiqRkQC$aRVSH-6 zLhnMxAmvOV;x1Jqd1tW#)C?qyC$vIr1Wp7h%86oCB(QP7F(Wr_ z>Ee-`cP~r#@Oo=T;-GP^&QW%j=(%jc>Mw&yf-wn#km!oi_b8y4O$f%SORMmx6dS{Hnt5|%rn%WSN!UwnI0A>7T`8v^clmLnh{ zo&T}8I%LNii!2J5eA<#MmU`v|hpXv^VliE*i$dS6E|)cJ-d3Q{?GByqQHY7A-v8du z0JSVs@VA9BcaanRNPme|HCcnEz) z>=V{hGeDmbB>+fWvwdn@eX)Rm<`dk3cV4&0e3l7|8QYD4s}wi;j`HZd&cDuZjFJ2B zvh!`EYfgtX4O`P>-6FX$Nd#x2eCZ-DhzXf@7E+MmMBYJ206d(XPG!E(Xg^%Svcg4M z9DLqj*8U_x&m7XKSxMTurSstN3GC1tm+IZoQO_3H3iM7mF;RAaQRsWNpsF?zeVpKp zilz=hN#r?a>>=woHbA)oU@^vF^4zBc*~trhck@-_oH4*l88u%63>9jD}wT>9>tfZHpk2AR|DwL z1F$Oeg4HM>@R~G%Hu=C4C&!_`(lb{ndIkRCa;huvkCG$RJ+@p3d;Emp-$#*RL7~Kv zJkxZrfE#Qb;jPr(d$=wkK-j$XJ31at+rzI5TY{$`J*3~N#U#^vqbYpp?Kk@?C^wyO z=$&ODCJJw_$G(u;fXS?tZ<~&gDpD!R%R5~s+SB>G`+9id+7KVA;QJheC;2Stw*?Lr zLsfj|t8d4{W6p}}c+$^gV}s&RK#~!A`^+ z7p2#{-|svgYl?r4+DHIjdCJx-WZ+lPS32Ukma4DjD~EZ~)1|RK-a7VW_NwdQ%mq}$ zL;GumK?c6Fc0aH`;AsE|volt&`ez$*YW~MIygyY2*70G;J!Ij>jE-~A)jCe^{^CtR zG5CGv3jcin<&|~F`f=78Z>yc*2jk+<2t?y)aFYG?iXSN@&iJS4Ziqadv%r;52wqDb zPM>)>V^+p&SYhJ-9l|;wb_bjr=%S6Q9?koAZp+)J>L?UXqb<6}i=2~9%lwPve94!+0o8xOwwh?9c>ip)u8PeK#F!z&h z_Kd13mY&{q8EkZmOMR>PSK1#6(rl}JuZ%kMnjn5g?=AK^S z$_!QaT)!KF+HKlB5Bg~}zf+GT)rwUd@hSWUG-uUsx#)*RS@Zw(|7$m%g8~hW(aW;7 zi)^F#GF@Zi!(_xb+)z>te3y;|HI*UCX*_#RDTD)|)-BbsbtpA`SNjBU zH3A%p?_!7q-@RWsz_`EFi&OODq&U9q;yZ~}yVUc&lCzI;zNzNPTy?ia8q?fQ|E=D<| z%LWjJSK4$3Tq#CYY?c_M=iScYtnwAiW)r2l<(F{mCLF=eycp8W*H%iq8h zlPH7|Qkv>X3fdH=YzcM3vN%*l;*LLRRBbPBAzyc7+1TE}Nt^_LcC@?sQcZlYa0_zI zNCi!baQwDByD)w?$hQ1s>fI#431%bpB^9+)lC8_LCIZI3p0s{${hYjq=jYFU2Ui!G2`Qut;PLT{dIfb$Pn26< zKIbNGKaF!1*59O~6&l`l)Z~w(|MSf{*w*0pI?>l(-=z1cj5Rwm`v4r}LO6bkVp!oesaMY-Bt2c2_ z54-JA{~$&u0Qu@&M=p&2;2)iuNf8PJH^9zu>Mxq|u-xSRO2Nn8nD7N{ciov4iNEU# zj`d;p0*^j7sIQuF$yV2`1AmPPBC8}f>vE@f#&gjeTW0_x^}1_+xm1ko#m5EA30)k4 zzN^KXbS#};6zW}fr$5~&+D{y+Wpo)U0Kg5WWb04NuKR!A#BK=n37juh^-4aR{4NoA zV1CN`j+un3U}EO23;=7|gmW8UYh$}8687GDS{xGYN@!ynDl@}HXI%{zbYkGtWQuCn zv+lEspz>47v#8cIW0W<iO9r0@wGy}Q3D}mEmSSen)r}F&h87n&UlIe zy#dtqAVR!>v#(V6>>a7ea+E(lmOsE#;-!>MNcL~h3&`xHhg4w04Y2~y?aVvgTksiY zXC_|nnbU)5-Pk})P7}<=qj6unQ+Vhl&)N?f&mN;zp}x>Ict18xXvZO_CS~e;{qF+cYg}f( zg7GOQIFSlOm)-p(f=;6lEnFID^+k-fRFg-ZuKL$ixhwM>o;sY5*R0j?CqAV>#JL-S zonL$^Bf_;qe0vtXhN~pws+w=vi*C{_Qg^?XT8cJg@hfYv8<1b_(Ez^yv)}?DaDfq& zGKKyMwJ(z=`-{~YD@n*dDdH?G`h#lDar-M|AhImjHOtvMe~PW=i>i?DNnerba>eS3 z*+`%nJX(cn$WM5>k(|%?XvdYjEKmwtWdsAj(#_(JXaP6~vKR<`T5t=;MqMoN=HiiDv9x_BxrT8ysq<5YdWDKut-v$5~9xte-#Y zm|+V%*>>+#@P+Yc+mpq+4+z`*lFGl=HS}W6ob1DGZtkP`b_oBGact-i1UNOwJIaQZ zF48Yw+P=1kBn=y-&3{lAsdLb{BbYc3Eoi6FX?ED8l03&%wET$bmcetD@`u(&7`BxV6oM)^-<=21lH~(>NKRhCmc9WIS7*xUPo&gJuIhFHsoW#9Vh#uOQDWh(f>K5L?e8$i-QpZA zL!Fo8^>R8?YY({^VEw+4v*Vfph6g_-7_Egz)Soq!W30X24%5!q!y7NYYNd7BOwTvH zK2@uP-+>>pCbh@rc5qgU-YhK&hS^|GU1!!*=6^jF!a~@yjYaxwm?ZA>jLU28;E=k9%x(LlVhAw zle%jC{9d|`c8EDKmdX_!xFCZEn8KFXaNX!AAi9mXE#U=F*5O3AAC z!ctVx`{NX?tm=S}uq7?jof`E*UlweoPxpXqxRvdJ=Umbn67RbM>n#(pSYW_6;TXY2 z>DmQX?3x0xSLALCH}5N2^07G;n$)k5K2|tciruXBiK3CyjLV-Q*zB9SaEp)}=^8&3 zUvr#g#FV+Lonev4O7*Tgpe%jQE*#U>pU!`tMi6oDAO2>}7OUS-_34tX^ij*iRLQpC zz{{o{yRS57s+UKi?irlGmr!6c#u;Sei^F@z=a^HRb24Q-xUs@p@+*{`$(L6FML*69VhktF2Jc4FT z8f&MhcP(qP!=(;~aex@AW!^9B2N073^Y8XBYMI~^k2lER8RsbJ^+7z&OA9~Fgm zk%@@)Hs91Vc5Mu}U?6xvQ>pJRDOGkuH0Es4v_9VH8j8mmGXY7&u&^{hjnvm5B2-f|v?^s|iPYNTT3+k~=Pqs& z)i0yu0iN+~lF=a2wd+dTHe_2=cmpt=`bHz-=RcHf@IUqsXBG8WsO_u$psaLA+Il`^ zZ@K!2F3>(p+Ck4LiEcbh6jK>HI3U?|( zLqa0n?t*+QZFRF*AJS_l$@xOq<$ZTJJpO2hLjB4azcMk~M~Qd3KEs}iT{}>`7@>Xm zurK4%2B^-6nm>W0(+Cc>*L3!_eo&q^V+Fof50LKpIT9`@&@>`i*`S*G%QT7St1E95 zIJNY!!}}#paV)=z;2jF#B%lD7x=cBLb>_%p@p zMFq9;hv;7l^>_NPBjv}tq9Q`}4i<<3Wdi}iK%R7IA1hy5R}&F1oUoi%_64fM_$8SC zuaA<=?_Oi86kZj5JANsibuz@|oh2+*%5S`CXbI`rY+%6(xgf8zXaVW*C*iq>QvY$4 z>#on_N#@$_(ygA21VDi$%1?J$w|PqOeFRfvQY_fwhdrEW z>$%c5w+DGKH}|lQ&4W{Hh}&v3SD6)U+NFs#lo*a(lm`C}-b~XuhE0&5QAd?g(v^uS znWI5=o>B1H9-b?;hHfObcZ zHvmXwP^aMXHpI_=qYJCcP<~0p?82xnR)?ZC7Gd9ETmHR|JXjag7p*iH@W~p@H(ly% z*m=D!zCG3RYx~=DBIw|l|M4ZS!rzpGbE#6=YxnGB95tG-?D0%iY&v6;n5T{tnVUu+EgId8|bEO!Al} z!?++FQVx<~HGE$lS-2he#2>dzdMIB?R}vr5q} z{{yzK^RZOs{-uC-`BVj$`Q*!jK|QIM1!u#7A%{X2(zt=f(mteqIq9ig(>ZvS4=;y7 z%=_z6|E!E`OZUy6C4=<5Vzr~;uY)^pksPKD06AuInInG&`dFQov{F#i^iCK zf0Ns8Kdl-Yd7kx0jA+l}kMCrKXGF|zT^{$l@2CgYyS8Vh$J?!<`yJBq4@m8$@eZFR zS4B9aISS-=`HkZFa3YE346g*u!)b~pQ(L6|TGY?tdoV9zlk(=lfc zi2XZdMy2}{{@00quP+C-09g?!kNa@y!G+AL#-coQFrbNc?G{IS+A%U~$W7P!%O#EP zmB2brM|xU6Av4O$MZJgvZ-@~8Vw64|R*H}_$#;^6I)7MWqF?`>tW#+})AbNq`^EIl z*cD5=<*D~{0mk(s!}tLh=2xtekC6q6ta(M4h==)B7#Now%CoW>bo>zQXe}{y@HjgCPunH&s+x9c1!A(SBLJgqW-W+&1f}7Nfn4ljbsK7g9J?kR zSV*m|$8g9U@_OKUJ3Sp1K{V}!h`MkJpHz`4yZel@iOTA;EBz_cw;!%w_0z_4ToU7S zfuqtj?}ghxbLI%}shkzA-O#PoVKR_dhFtD-G13Z3NN=Ft3McQm*z?7S7p+Yf&CRAC|Awik^3R8$||Ckq>;TSSVvjm$fxz zI!O-0=Z!i^D{G8$37Wht7{k8t_gJB`6=SE$6e+jna6T%o^}@nA8^w;Ne=PP}I!D{!1PDr+WxWDD~AEXax_ zN(JbB5~ni2!ef@v2A^c+`_Mnf{F>G3cauXH%&lMiD7sIK7bb;ZAoj(&9rRVm+OU#| z4)@hr_6-RIM-8{R8OR#d*U|wCEoKk0eI= zF)JhIq;ZJKw0=k+%uv$`Rtxu; zLwE6s+XDAL&|eLlNn-g|(_KUig20%QJp&(@s@HHd8{7tp&~%6?AR7M(8ZrT(UaW@x zaARr;-217t6e0Cb_dMdZn(;rORKw!x27L6j^&z1iXQGB1VT1$LtZeBwA#M!BHEqaX zvx!qtV9mDo{7h1rJkB~Yr|IPfxAa7{Y>7+jVb*08iA#gjL(R9-B3Itugf-*p_(w2_ zr%iEJ74u(SDFu%QT;KWWH|yKA?~Z-=*AvMjZ5S3Sj@}9J7e?x4%Dzz^s30&bsUD`) z6c54aBd3dJ)~@>y*^i0gWyQ)@%A9!mkIx(D>sW8@%*;#vheUs%|L+66xX+069o%sp zs?Bw_`$Qtp@Qc{DAQhZ8--XgJ^tnL{0Iryq zr05wKU0>kA4%476!FIRwCL5rBPfrQI=cTyj1*tTsLnA4uzB}Z6jwuzc{Nm&c7W=1R zNV`PPV|_LFVp-XY*Wnc|Nj8)io6DiyoM1;fkEMG>kG;3(cXGQ*HQj|OKebmfUJKAj z7_AT^$?NpCN>$C<3L;g%^eoNAQ8zt2h=802P* zMD;o9?|`U4Qn=@fvhC~8`kZy5DG!}oEv^WIL~M}s@K8cjaql|iWYg%LWsfdYj{M=_ zzmeX1$n@VA)xtxyh*%6{X(6u?rf}Hhn4{PBdZq2G=gD)-i*mZvl`lf$c8Pv7-S_7b zkg)BFSB60&waYYQ>Uu%3TP!ErS9TKIUSv_VJSQu}|0|0pqsV6L-{IfuaxC1`q>dd* z9jrjRMspG0O148P97`jvZ+uO*t07mO^8XKGZygm^)8z{{-ndK9#v!-|ch^90hu|JO zSVMpW2@u>RSRlB&JHg!v?hrKicX-}!?#$e|YrRAL!Rpnr`c&86ReS&B;Lzx!u6$Yq zU^Dx<1ncMAVVl4(GFX8LM05{vK6 zHMZQJsn?~1lwHqM`%BVCBo;FgW9k4l96Ssg!vI+Z|F4ZjYy2 zgssKiSRK0k8_D3=ir0zj7hQbl9hzn8z3@L}(}GIqYfM%=Zir4Fzs(zNJTV`&;XbyV zfAqz)3(#qbJguPV_JzC+zj<<7e`EfYgpXsU7p?`XIp3^TL52|(0=Fz11fcpEhpAFm zxd^{Did8Z&mX$r8%(Y|pc&2_w`@)I;$v2Bfe)o%wY>FL?bi$E|CmA>oNVFJOAIgnX zvs;ALIV`7N|cH(lh;dp5cH3 zHNw#~jcdB~bDUDfpL4pqvO=!5emlweTi`@c`mSh`DAZ7K(t?GEVD~K^C#dT%xlwP> z^@vDEik26t&z)zl(fT*+VvS8McfZ36bs2>U$!lV0c$VQ7=!C|UbkGN^DM}8e!xm(N zJWo~qmCXB8vd891zSgn9`P{-J4|O;gOl!i!XbdhpW3eYF-KG!R|8+8Y-&jJQc895- zNI9BJr!)1qE-T6-uXg!yXHfRirap@f31Dl|!Qrx$)ee~aF}E8N*J!Y-KZ%?q;5||1 z{Ta4(Mj}h(DoIijV>cW$?LnABICb5lWrvE`*oXVlPt>??tmj#;g-j5%*wzz^r^b9J z4|A-9aKX8JM~MAJAMq9PwAU*_0Ix$tqp;!FOYrx7s%Ti9?76%UCy848#WnAYr&h^x zEIz~1+}Kz1V**x&>&ezN7Fo|#gIVt0C>YvVMLWLqT>Su?nS<@IECPR>IFqmL8+iH< z3*34IgB6FZWR-5s$oy=d)1^|HRsbq?Jut1KA$HQmK}h&k6ZTxJ!t)cKqq0Z1sQJ(SC@FZ`52_ol6%HgQzTQK{_y=0kqRffj3+MFn0Ri+l_VlQ0 z&a`m{z_`hg^Zw4tuj7**G0ltupR=?1gKFN^eUT2i2PqSYCbAdU;`}7fL+GuU_QqWy zRAaCwwO*`wHDh6yh)UYwcwLh6L{zdX`h~hKlqH_qM)8#}P~K`g<(A}_r-Y4v#P9#3 zGvXq&enTpTV|}u0-O^t$k~%S??a96`4hkmVj=Htxw#$;eNAV7*aSto{5^(8SwQCSd zZ%=aVw|(`Oe0H_M?Kfsg61~d98)p5)OLF1zoWHl~=sFL`m|bROh~+yvyyn|yO(#4AdM^4&|l1*Mzz zqB6E4cTm}97Z{iC6)UxNgaE!vHW-REhd)M)2TvDv6($_Oi%>!_NK)WSzp$NwmU>7X z9qHs@44V61kqEOu&^k^-F7WHH+F4}JC??YeAFoWzs5GtXF#z2Woi)8J&WKB%&4@JfXvGhp3*M8EQ3_ z+7;(DHF{7CZkGzBuKjPKAOtW`@MNQ9_0CZ^t=TI0H|%mA=Q;H~A z&bP*GmExERCv~3KAlMS)p|ZP$P_Glm;LV`^ppFOnb<4kGC2zIe)mAgdI5I%&rjzrx zeJk+V-V(!obDRX4b@(_PL{QFbFPEQ*sy{5wS@iu9+b)LTEJcqBhc#*UqTBy4$Iy+4 zD7X9a-nHhUF@H75hrgSkMzWb}EB__pzv|!m+HiInF2>{NDg5c_$whBy?qRi*S+2b< zKI^nLfIPRoozT4(Q4M3A6kPUqJyun&`Bu>RFYHwtH37}Xi@~Ak*4DX9CZyBwO5Qku zpt_30u2$%1by8c=h04T0w5s}86zx6<(T8HkHY#maZ!cdniO@jQoP81&JVq!7w-@+KnB`ihaRFRA`)@P7ab1@QU6~$ z>vH_|(hXsLHgMPC02|C(xXq_j^+@|hRG;_4z(f4VfVWxiEd8wIXg89R*t*t`O`@pC ze9JJ4#})B-wUudy^JAHy!|P^ZOO`(NCV(M{<}u58@-$vfBQf|ZAm~8xGPUTukf1sA zWT85#(JjV3N{en>OEvkY`6vbou73o65&F7yW?@qsxGH(_F%@xQTxK7$Le%5eUUGxL z-UX;-{jo-zYO8**{vE$Fluk9%I*Yge&QR6kG5_8K`f4v=wz8I3>@F%p;1wTV!H9WZUOBlPhEk#Y;m~D__+glrSmkk8Nt2LQIR$;S?H}f}r^KJs+@MRqpgIYVUCx zHE)u@Eo5`74v!i(5vvjxs%8y+@5wVPwPn{m@c4&@0ow(3wnXJtM)EXcPf2P-j=9e=2Z!dUDI&Tq zR@w<;)jSrcW}&!C9n9z8+VS{6Fh?n!A9hAa9;@~Za653t*mV3C;5%su>>&DQ;U91? zVIvy&T`eaZ+Xx$myCv}3I>qapr)XsSP25A*=l9-;t#i?B^NW8JhdFk7z~A-(jnT@) z&Ymaog0n-+8<>}WLALp?l~E|F9o{&znuP=&%_F6(M;E>6%?kX4h#T}Ah;`}N4!d-w z#9fgxdjIJ!&UhvgCFHa#0bX9$_GOWlQ^U`7gBYsQrP& zi{;!2zj*`g#ZYaWF~NL+40ASr9cgH#_B;q8i-G>SkNcqnXl5$~KG0Rb$?-^1OtCmc z^GyZAGIby%d8N^=a19N#03s6G_cboM7WJ3(l11g>Sj{fA{N24fnkA@O5jjZ{^y;!7 zz$XgGmg-4LZyZ;exxiy!)~VZz-G!A!Gu&moM`XE@Bxekh5ao?)cCgT1KY(>_BLtsH%C(IvlSAA>{RqkrHAAmNFx7WF??E=AOt1%q*wL(JYuYq1vU3~KaiP@2D=ci zVS~;2N*cJYDA;3GdNjG^GjV!s%@>d;{WAm}RyYx0^!}A$(nu7}WN$<(czdel<7<9e z0k9$xIsb>`)N{TtZ}K)pAz2+WThtCjbP}8moWOBHR-<#qUy@|IF>MqxfT6|_k*CCYk>yqf@FHs5LlCX4t`~SJBF8AXt86I>K$R(WdoBAdhx?JIP3VJ1 zg?E0xi8%=nbVV%Y5FeG+c_}u@JLI4$RkPyWf^@{Q!C&0LUA~~iPkXpOwSLMZ8L!UH zEw0uu{?x{BsU?PgNJUwRe!&GgH$c&@$S@kd+C;d2`b8}rc8+DI$k;V2$o>2s9qP$j zf4Cpkk*aUNw_csL)2>XXIo;D~7VD&-Be&%h#fi!{`Kk~=#ii#QkG2End^LY+?egzy z_x&)N>bt7hsHWxkFrIGTpRZN_lFGvBx5Ivb$v?41bef~so$N!$79TTO=OxHKeiy#$ z#*D0xI*?&5H0=((N9C9A_qaOHlz}1~3sdS=*#1K@a5t@lO(OY{21THw>%YG~$#+<) z$L4Mns|%%_tVtw{a_Ex4kGrW5`zTNVKSU)OjAyA<$E0{c)WtY4iBXI)7o6Ax)}@Um z@Ks1{4Z$bi-m1aE@t)j{_RDx5Rqf+R34u0)bKF*Sb(+NW$xfAt7PK%-Z$D5!lGV|{ zV&Thx9Pj+>*kImIUOs%nN5{&as)+Vwd){VvTrpiy(6HNx|J*dS0>ePe%5h-mHNZ2sLTHA z7P4;{dDE~Cqx>uTLTAzvTT&#ZNP=>c@sk{#aSU%5*57)dIKVsXJ#r7}9_Af;zXJ2c6 zujHapuG_l-h(pKc^5>?g6`*Y75=cl*ez1-(8UKgi>}{twaCnee@w<&=zqu<46T&cl z3>@wqi)T1Wi)3{qVn_WoDrMAPF7~)334m3CIenooS`K@>D7+e4vGvW#ihU;!%P=Dd z_G_BgpHEfkTktyd;y4IrxG3)u+tX$ z^VXh;;&W9~GHsZ4tv@6_r>j~%#f<``L39t(C78zcg1Jesl#i2`?lQUF4y%^kW?K+g<^b`#*9w)Bs-BfN?iSF6=|B9S&P( z>W9?ZBjR_TQUG7eXGGFH3UK9vXHpD53Y>lv*!1ym7&q$nc}kUW5E}$05!&0I+slNIt5&?gy2;o zuJh{FpE?HnDE<)`joZD(rh+%Xt9o8e{2WIEw{SX7kh;Luj?hDFc(2a{MRe#w;&!B{ zoXU$lfh<^3BGz$T4)Ana5N4$uag2JHnPP3e#nW+<=~T@{S+Cb~+=Sr|V&g~=kQk$7 zBm`DoZ!cKPdg@eBBBh)VOiv**9+THK{%VL4T+2*D9aJ_eO+W15nS2&M>(o&7eYSR1 z2(dowLb&*vtvvZYq9sGXJ?G=qVgEAqXzmCWP1gqyafx{F4W;H2{7jNHu~&po2yYup z{IrDYbkd0+Dic4mS8tQbA{gDhN4269aog9({&(?dZU5`H+I6odgWo7s$bF10&8a~Z z9qcZL@jLr$JKd0J#nBa=S$8a;s@m>74P_X6sG>4lYNTl&mU4R9C3?I){t+QmluOX> zcP9+Tj(+$N6q#p!zf2J|xnwRpjGOV)GsZMt(UyO^5>c?RXvdqLD{CyGbXebW%{F1M zYBY!wqLdbuwJxCq8^XfCHEr&PDsX zW63jOXGTMVZu~!+`>$;Dmfr1*-AoO!nGs$v8bW#{79I4dH1~+`Ujpx1bW*F~x82-{ zf_Vsy&Ae?+W?t^;-b!Is<(W9~9Y0vf?IjEn99No1oF%g0h$@52$t=mf0p+}yD9GLUrnq0MMpVw)s6>Y&7ZT_*6HLSe ze6WUB(O7;nW|E(?6+MBpI>ioM1vC%=J<|p2_lmTdr=Qb6v;T^K`Ah+VYjW1*$;#Y2 z>as|zZj>?_$`}$s%Nl;q#}09OUBgc6a+ns}rmH%^U#xxfzerN6YvX=>a)v#f`7VuG z^{XgPXa`W9d5&_fn))Jn1@X?upWGy9Z#Mf;ERgk+tW`5bxZ=+TA#VzHL@8~NZx>l%sofO^2u~SeifSj$ze>m3PDs|A8pd;AAQGR#u3(3DWl6CPfRucZ#<=qMJ$uD_QK3Mn$jRGfk2K&FgC^a}fD z^b>Yl?jmOeX3{zhcxG+nUn>!R9o8+(A&tY=ducM^=dkf*aV3g)WM*$xS)jaaPVC*+ zs3XMz(J~kpOzH75T9%Z0RNjk1-xe7(D@T}J&N}@}E%0l5B3*f!ddwLKwrlk$4S&TK zGT^f(x3T7GoiCsH_beI>ewJ;>k42vsOntNhrZ? zpA%HMK6(h71`nLf7V5a&noO1K67F1+nRGIXB*SSKUv2*OG z+|5PQ0rMB~rDz0Lp|I(1hUAgaa7gGJx;6UZI+KAI6e96w>8QlVfUZ@C+|ifsXm|iq zO!HEF57h)eWzQGg1UZv!RTN6pf4ibsV=qITsh9vLuMBLBep$ro8h^PL6XJ4d{?b;5 zvU7Yii@mOgWiyPc!z2-g>4#au0E3gu#>hbP2YX@_G8d835WP&k#x=3CTtZ#n&gRro z^1csyDNd&4zONywvX1@6n_e}2>qhwJq;8AAviA#DANCi}0SaIK6o6KNZ1d4hje)Cw z`<+wSA+!kg;mmLfAgq#MyfHbL^q%$hDEtyKv2*TGa{@J*QAzNT1C zqgEj+kr6j7le*B%H8dv$TFEctJP}R=3*RO*pah079TzgRbY6~RKpC(#rmM7loa?6T z34B%XE)=ey%X@P)uLH(fq$?wn&YA~BoaDpuJO{E}sUm;9JMf|b5{zZu25#ZYP3vDw zSNv&+EXJ;hK!3Ev<@@>f+KMm4y{tGq``70!=_kHtP?w}17Y%+dQyK9Y&KZ-jYwc~W z%uYDMLT|UQCp{pP?T2yUsspUBaP4>}n9v*iV=5~?S?qi!Rjr4NuTyrL8E75U0tU3s z7T{BisQJ6#abznGmMG|4QZhc?R zM5=!{xz81Q^X|0Ox#RPT7XMpwE+o})FigXEYIAv3zWS3P!hVb68qg@rUjL9iSKF#= zfYA`<^W5r*a`LqyQ4qsGid1Y_4ax)QK`?0jsM+P|IrT+9lIaW%31d8d5EqvJF>h}X0Qf!@V?`fa zxbIr`F1otiMW!I%@1B1w6wmlR-EK9QY)L9QC_XWlnV(>s9wG0E^rB-Xq(@rkZD8)F z6mY#mb69IT-e{5c)Q*Bfh=e=)F`jJ-H+29QT+re(A)W9?5HKlx_GW=`E{3RW_9_2R z`~9OI-1ANP9vgS~8&JahYlumZ%E2X1&Z^Hl?JyVMm^BXnv()c_jQZ~6V%c!w|6Kon z%)|fdKZFTMB9L*^cC_>L(7d8IfjZqnU8Y265S)^q`x9yBVIcL(l`DRR!$@v&%dBnI z03QCk5%};c@CeH2vPlT@Bl1wBjnLkY&wr@bSiCY>VreW(FfA)9+%D~<77OKHhZdo6 zO>6D~3+LaD7#yTnP=Oh9A=Nsb zh)pq!55N!0KVYC;`+p|wKPK=0^cuWkpL%@wGaxvHR!vF@UG`Chaz#2@Tc?hDt}Br zedH<9sr>#VENC)h6|&^=lEch_kAEFa0c~#iUww7x7z~`qt!xWihkTU98e9&1c6gBb zz)8kWqf(p%Hf(L@R67h`QqI30VsBy)k~eVuiNzS;R4o<` zyfloS%8NeE{FsS{D+uoV*Kud=CGLzwVLV@Q^#5Y~27OY4oX)Aj&*D)!@Zb+JSyXI` zK5#vIw|kV@0uCX_yF;q;yLB&@?L7)4-0fRxXY`lF^6!TlDf@b?073R3jdUO*8$Yas zw;Z0ou(QI!I?IOgv7ob#_8y=Rd07hQk-)+m;+kzneJRKOW6*R5#?mTI^2`}1ZVx3D zW(azY1M0n4#*_1x+54XZj}!y^BTt_+jsT(XO$u_qW&e_bkvx~PCgtm`PA#brl>O9| z7fL1&@MSL{**J7E4;UV4wfQ160TR;VwRX1a_acI?4I0=rOAinfg`C$xepfTMzO!8q zQ|W+_qIz6`a*_Q))w?`r?5T?KzptWi_qrdZ1`KTFPeuHSVB0sFZ;oDM<2#d@t;2YE zr%c+fG(fCcxiaVni?u!`ikRdAa=_k?DYT2Du`9Mclu)FcR)=`TXjGDHJW-;{1@`k- znTpBbQ=6cY-iu&s&mPk*M`<?hD;XcSS=;`z$HX7mtXvx~YWLLBS}8Q7mal;S zT0M_}Owc0|C>ZLgF8sY zg5h;t8}R>0p65>`J$QZQBmZ=#9pi#VgF%qcc4|SC;#&=TjZkWo}6+j zH&5aJ@7af!$fNkN_|etqf`0hxRxbShX1&9T!o|+E(_!r)5{9Kk_PN{LrOlo7<&Q|$ z$`=co_UEg>D+!qW$D37T;3xZ^iK}WG1g9t>w`GMVhJsu**7z%ct)4-8kN}jxpBwnP zyrGC^ShL0U7yr%XKf6i{?n`j0IP-K0m@2E6>bd70XRL`wB(mtgZDuOt_Dm!R8iLnN zF0ZO}@I@2!%7>}i&N7to)yrRs#T_w`Lo_Q zrmft-LJ6ip=fw-qvQ*Ds(X?`*#)iwa#bp!m&TT^CIeke!8V+bh6t1@3rz${%M7_=+ z&(bV;i7bY(s|Yv@hV_oZ2eYl82l;)jl@=S#Wvz>->@_vZ{o%|LsvMSPgYg9b4EzEv zKqBQ^KmAZ5i|i*vOpa2W&c}P=lidrhuMM{Z<|_!OS4A3hRxL&AvVgkT{6M-rCm`^Z(vfoo!q@kYc$>&|vXlO3ZFrkI0MhFce%2Ij&ygDI6a#4fzv*6jd5Z{}r&Y zFEba5HvHZLd`#K$88sbwv){7nAaZUd-;S?%tV?YC<-ojoWsHW8(e46|&t|mT!2e*f zb)PwS+3Rf1xGGt?SX*Q@W>jzq*ehE1M;$ZH#_LrEUHavFd`?m+Qb>3`<^G72Ok&jZ zo}*O%W;@^Yg5S4L_@jYj?cp?s_U+c@AO4LE=2ey&hovYz!`a36pPH)ygRx$w*vnf$ zJNeL&mFL8pgkqusZG-`z@OYlCCMasFJaIbnflW|?pWq(l9Ak9ij~8nrEJxBaNs`>= z{_hOhK*ZkxQP*nuGW8&}`se9lMX||q5~vdKiQ8%{ zGhY%3b7HYJYickqCE`@A(9y7XLCJF>v0Yp z6u-(u+qp9HjUb50<2UNhHY-WSmL%G!*@$L=5E1NUM92AxOcQCCq@dmT?cY|zi9`9n z>HudiwDcLidqth~U@sj8<61pUfl-I~DsL`_l_uJPND+2|S0kyejm7T@lyqPJI@ujf z7nb~tNx}O^rDOL<{?rp7 zIb{p`Tql3LQU&yq@EaB><#uyr91xBJd%)z%oAAi<_ya+967$=j7w?(R7-W-4^gY>~ zd|z+cyec>P4&aZ2HIXtkh*F$V+X)+>%>AdG`%{J1Y)MO_eiAXLJTK-+i?|!zD44!B zP)}%k^ow&vRfXErsn~oiuDom(Z6Y*PwuyO}r_0?8`y>lgL!Ctj4*BCZ>og8!AqXG8 zb3a5g?zrd7S0rH~^W8L^PByG}Hz9P)W<2U+YfhG}(6gQ?P8e*q93g&sicaynE$TU` z>$w{`si(ngv%0K)F;rU{dmANN?RiQ*^*Os8BR3qMNnPVac03dZO?|Vm+ZN~aqY6y* zeiSn7JY)w0T9lrL@{1x8Jt4V#x1k;2IstA={*8d!Qm_cc>5UKs*XDFP zq(p4N#m>f2PZ;pp{<{s&6E!LQhRcct3@N9me(LwvWDkl->rPD=(Q%NoiJ#J6a^Ynp6A_Kb_G3$IRF#GdG&ps|GaZ-ivotl~==!Xw@a?RWM=#N$ z7STTkzfODw9=Uc1S?0gDeb|1zbKiT@lCo>>?lt=PcDiV)a3q~(?`{x4frNGnE2^YK zBO2Ux`FHj?Jov)-->URrOwD=R|M%mecOQ%qQ=nNV6^(Vr5M0tj`VPCg#A#aiyGFT{ zz$>d(swxET9z~D#f4yt)C7uVJnMPXp);^@cB;phyc8yI?_+!Q4OC+!wHWO=ebI|_< z!L~NKT3RXneMJz_-Bv~#eYfsMppX$K+4QF%4LBE@xg{frfy5!!l{Tx({4#GG3O!FJ zrU)T}jpaIL&Ko`Q6p{DN({g|ru{xqJ6htI}ng#*(y(wuAPEE@g%(4;)J0{gMzsp9y zTd;UB`{eA4NUPv@$t+N&nKbzE%K}1<3RVbmWh$?ojJEMH@)AfvE|Sym^D!q_;_pvt zSb=3!cn`=C^>v(%A_U!+{PxLbUO8<&6?F_|ZeiFF9p?37un+hHKBDtks?i)V`!MS# zbC4up)kN(2dgfGdS&$Nu;4e%pca0(ZHsCf`@M0;q+y9l!yn}}nM6|csT8Lee7B`Ct zZxKiDXJ+7`SE!UyX4tGT@m=~@W9Q8tiOq5YvisirVES6eX-&`41U@M8l_)EhRx$|Z z9#Byi8;=If)H0jYB6l4ym9-4fz{O8y3)}?)M*ylbRa}EU7@Ds8FHQtn?xxdi!u|NR zi___$OBy})sMITo5Y%j@d0IyJqU^WJ(dpG@w&<6GSW^l57uEHA6t&T!gq(bqeT> zz~0NpcQ(Xaca&mG( zK4)7lY)$PmG%)Zn(4a$i8ijs&_P>-Z>)*FPV=L6`aZSRX-W0j{*&pY|%WqInmf_~9 zw{U%PJMH*KmN2n2QB z8#guQr?sg_4n%EK8s};Kc%M3MEyx`p-T+Wl-hxnU#$%tnB;HMtA7h)+k4m1^`rJ4Q zxbARkmK%Lis1AYOTMvZePAR3}c0X7SA?CGzAL}eSPR`b^SD7$REY0v^xkFsiObkuu z9fYE0z%-2NeREh>D~uGb8cH^Xm+_Y=(;QkTF?R`N3F`tduc{V9`E%q=9D?E0b>?#5a{FH@B zC)^=s*rz~&Y#5`sQ%&wQm@XJKr6fcQVtZz29y!QIHkynFR=DLEV(=`9Xu|UbY6bfQ zKD{|zvtsVVibhWi`Vg&{=xfXDq^1gR#Gz^%;V7qjfVXtE-joPT0p#6hXELFF9~v4E zY1E%5F)@_k!UNJ~+0~zbRHBNc@RfRxy{R9llko(}cOH=HRelV1rz{x`hD=X&S?g$V zoBt%m8tPR4zz56%pI*HYUd%|m8qp2=#*Y5Tl?gtF(<*6oc>w&E&@9@Xg>aLr+v z)!56RAgtYYq%Di~=rhzCv$%6zeDFWj7!UxTT}1g%lLTJB#drMN`ZcOv(eThW*{1p%|$vD>P}r_m!6Ek1i(XD73!5pQ*BpeDD?CY?enQn+b~cpR}nZ~PV4pUm!$ zZ{;!(7QIth%~d*s zppYnvJXNnVZhtE9Cmp^zTb)^dBHd&Jk16!o$F2MS_XY_o9mbgf5?*uH*F0x4?iIna zDC#0Q#;Q}oxBYTcc#VlFv5)o&@&y01bAdL_mzOrq7v+?(yxM+Iz#U!fM{YSv#HaJ4 zQM5dSQJt-umVzYg%Z=sfqBzNwj^Dno;hhVCn;0m))hX)GqZOR&651l+yQZeDNaCad zS0aEx7LF}8!Prk?^Ux2l^EQF-2FR>>HEP9N`ruJYF{B09S;r$f1^8=^5^5IkT@Z_~ zNcA#3ign=iIzO`e`-lWvG$F9K7=4!{iIPO7sz~Pg+HgXNDH@+CpjQq~fQ2{6PCPoP zG;C)2^+ag=-W~bJZAC^Pn$93{=SNt*ZAgQH!I0yvn4fMk984o3YltxkZ%JXVy>!7a zqHaoZcLEG-x^>;K zG|70TK34ipZLAVE2W|U3lAvYFR9N|k4q~t$&7JSk9bN}+9~zd}`O)&l?`FdM&qvHH zKq)u%TUFpR8gcZ<6H@`n>)GYBpJ+adUF7aDAMl_f0<|qY+IyJOoul6ViDNz!AGNYg z3p+g&$m|yDq%AS7SbF^kzpBd+*73+JC(6`}S$Vj(__ZI$L)LE0{mA!?%I+g_G26Eq zw>zufm}TZSYjFX z-jEI{p&Uq%8fW3d%+34bKNZQu9{MJRu#~LeE<11~kInBO@Z8m{+s$tY)Uq0}FzW|u zI=TKY&O!rS68h3%;}q_8ga%ohXL+?rJTa!twt?HPIuNjeKh~v*fXrzAIpJe3-#f7gozp=yz3BGCIkrY@g4w?UPIJ+$MOpl?RR37mTUg54t3?Q1I;L(7kj*xXap*z1)dO2q6()=Fe zd_E2RZUMQer;ZDl}t*gGbUz?Bh*=;UTqdoH-h6+J;DMlma&lclbx z*Ez(Dmw2j&EIp>Y1CE+-gjH$-5Oq*0u<+hD{%E&h7_+ycCC+)*?>Tal?GHPI5rRs{ zaq(IhanOC`zn*M5T-LvDh;^;woh-toR)OAq(|Aktx+w{#e3ChHlE;t|=%Kyz*Z~>z z!DMY(*dW$5cTlkDz|RmaQwttvoJ9JBOTYz<`1xL(gXidOJrU)kNbH)5!e+6FiXK{Y zP4=cuvByufpT}f|6AS>LN!f2mLof)pCUynKZxo6sH}s{MJ42N_A_Vh~0d5Kk?%xTQ zu%Jxjpd@NmT<|&w-WSr7Nd$NzE!1zm5vHA_6%h*#t=o=+0%p3$v>^S|^^aIVqEP8NCecR`yKL0$h|2MDuI8Fx zMbVh!$+wyG%(`c-c9UIZg0pd>Nk?rh6bwjb@*skhoAu`I>RB3xUOuEJTb!I9h=`Zo z3v;LN+^0E#6f(rgsCR9k0||DNs?CPqjgo(nbLdX6k8vbQ;#d9dh{aw3uId(kM)13G za5`1dE7Lg?fzr|{YQ^TTCH24^2sZeThp>r|CQh;&ys$tKJcY&#gJicJYDyi(h9MrF zh;~9cy&>Na$3Gp!eki&g12FxJ8$gQO-RV^xEzq$xfp587#MGkNK6Z$Un}V|Jh4(9&~|J8GS9& z<3V~eb6<`_&2K(QZSy+mFC5yNTvy%u!R%;me!R?y^IaxT=CNL z>KjSPVC@%A?fJmEQra+qjKjL*Za3$o$H08MPKN@^qekTjlW*`{KkW3wlMsB}X)g;R z3~TVY_$)pH8Y*3nU(i+XtQVoDusHEZ#XM{Wd{rk$LoZykn7HF!72Ir<4-(`^r~Bk_ zyzeNun%Eu%HcqLVO4Rvz4lo0Ag(vjK%CwtIl!{oW0%aFtZD5E)k*f?hDbbH(-tdv! z`hA9KC#4Ac&>b&Ve%jpj%8o$(^Ip$ zTuTCJ#L=sAuVJsMf|!+G!NWlUZ_r_N8jvT)2u%ov`l!K=l<8Bm5*6#+?i<)0uwcIs zP<&?y4g)xEqRXs-Ke)MDDF!i{`)_IKv%Osr5f($ocUS`C3Z)8jkA-7|tPZvqM2=(M zH!{n#J;$&Jy3^KkR=fc{=filTJF?9qarQ4a)a$Q`a})AuF#dGL8wb%W=!0+El`^0i z5~gi#ZSq61U93rAw7gl7n+~V_gC}kVGugB3PCF&Kbd!h{KvQn`*^l({2@J)?Ge_Ol z2O5nH20h-Fzq8%$vfTQVTwR`!;V8Cz>FW3Wjhl?Y>hTpzi@E(g@6ybDe&5$_;?Hd8 zLN6-)P9!xGwyp+mbIFZcDFvyvByL3nnhKc zs)5z+C|O@E+YZlv0p(v#p{%bk&V;~tXsFWa-nc$SWET8{Mrn;P_b;#;0KBmyu>xK) zu%HHgRB8~7P6Oh%x{sA(0gqP*=B?(1g+8Rz4Cdq1&B??Ki4Ab5)s}%V@Lf3i;21~2 z{1&hh(7f+j?B1m1cVjB+i^cZ{*Sbt(AzjbdJg|;!5D+&>@<0bu#y*xH=%BuiumiR1 zPv)nEDZ-7|rrB$YTzi)el5CmD>_*dL1xsTwkBT{KAbGk=2oN0}D`gt}*G)uqeAK*KegfUW z@6uVUkuH*YKqozDiA@blE;ppNE?;xp9Y=Yp3_+Xu`IiofxO2oV7^Nz45~3CZ((dSH zH*KjYW$8Dt>#dvSxJ1y3+R_7q7K?-7q>QGja$C&$|+9kRlwnc^|eZR;) z0a&G7&B!B$2n-L`NOn!->tzMrDC$);k<<^&X$QLTIq#Lf0A z&2wcUJ8uNXg_=dAw7(}9b;akpJ-u6_FqGxI2bgtl56jk1)!J;9y56k(Au|`m`;he02j>62lwP z1_o$RP`6`wzFsyS=71P-7#lW$wSJIVHx*pj1*b9>Qr!}#{E=BUhG5$5GH&yDL>7EE-U5EI?@R!<;M9(y+G+rc2Ba~b_c=|hC*e~Yycs2)BkOANuu?=w=a5Fy;O%8 zwgxrg6@5r32Fd6No+3h@UcAsXZ_1Nyl5(ghkS!LE_o+_ocpVmhq+vH@sXWJpacFe7 zH)CQtIESj_o%{7(qrTP7GgZbJ7aEYZ)1fe%n*pp+o9+%U==hCF>HtyMeG{;=O^M!E zvA^;&7*E$|Mpord&Jf<^2y!f=nnBk{NCGdOSut+V(c zg8bEe5UrLpB5m)?REPMdh14>ke$+)f1C1^{%l+8irv$c5BAO&uW*jvk!uS3;0~jY1 zWnsLI^I}<5n(w|(SJQvuCM9G*%fmKx8NZL)q_` z41XE%vJp3uSeR9S@@Z>!g78PSIjCxlZRPn_YA=U|M_-@P#Id}GF|+twxv|yXMEl$9 zhjYJtF1ns3t8^KVylYU8at4_d`rKspuY#{6))U{4+7b4LH+3ry?&bbzt-Mr`s*y`m zs&v76K?ilFq8U%JeJQ|BOQdS ze{Zo9;v-Ez&^;;1Zi81_f9PyF_5sx+IH$n`;~=O_IAJ1 zk>ALxUTpMU!|{xg!XK~u9N*t8PQ?A0uX8*m)*F7d>xPG;`(`pQ7u=#;uuW59ibIlf zvW^|%?2uf;sobs^g^cFK)|fUh9><+YS3K^~uaW_r*@}x4s_JGBA^N@FOcJt`|)CMJ*^L zCB-ecw}_$5suOXJ(87bOwfV@bI+Lhh1UMmlMj5kSGcLEo?n`^Ha#*Tj2P#yLUSV`L z1&5uh#h%7ZtuligzHhHQ?1n7~AW2rpF+YOUf;9gwo4WFAle9>g^xih2MMLHjN7Aa0 z-GCi=Qz%q;=31F~UHlR)7-54CW;O7vDbV{uaLcLPl{n7QUydEg$CH3SD=20P&GMPd zxgK-K&s!wz_-|ML+MiFG-Y7n{*MkPU{ncz^6c@KghfW3FzAWIB%=(>cF=nKLr%5(* zh|?AuDaLf1jp-1p3H2E5xnUOunUGw7tCxyrfpA>qisl=A*Y@Q|ifS_BMAr-7wuWKa z11CvROg6U?{q{u2hdvh~qerFAI$CCKC#z1~wD$H>aPR!x(Hh#ooE2f_FW1`}3XGWn zwz!6>dVXzB)bMUj9M$+oH0y^g2meSJDFko-ASF7Ir(nwiYHJCHisDQ%}DmSiIX|0Ul zg}m-)!Ipj}O(FV=&znQO+H&M|q5R(Ca)5@x#BGE#QO49jnq;bJ9K;fmha2^|ez4Z_ z)nZ^h;vIC;jB{DZ7NZ>9%PBe303NtW;ydZ{icDQQ*AQP3q|(u8*w)9`R-ZG;*C5-uW4F$e%j&hy+f$fl*1NY=vYa#{O~KTCrS|x+m&K; z&C(q#@A1(S!{)zFODbLjO`K%mxI%S~3ld@S_ocQ(k_Db|{JPO$n~knk7qx=Y96aXf z4Jx+|RT_KJab$}<{$70@`nWf=AG$l8GbmSWxh~)=EHgI_TjacbB!mF!M==U|*r0nd z-)d!(q3T-TU*)rzh#rlsWrw+(hYGSygfZ)7)nujF8-Y6IXCGoRAumwG(6m$9xASuQ zl&at}b9V5}A7ou4hayHsmD{d&`PCF{LRlk^zP~q5|91Wn3K8bHf6sb<|G<$o*7jtX z+CSHYm$?SqQu2GQ41>n)U0_pt%?;CC^*3pohXT89VRV;XZeh!_UnzV}7{^P^ z+99~d8qPj{dOYLaqNqB z9Gg2`7Rnk@Zax}1s~^+U##Fni5LOE~Z4gL|M}CQ&VCYLAWs700w_DU&uC}9vUo_ zQpTr4lQwB5(UzY9%xSo4*LOPoB4xp7+U|ZV-NDE$B#`6=&Mi!KdhZ&fq@Negwe7kE z!tZ`@GMmR9uIGG8R14-jKGwi>n*8uYF)#Y;Z0uFl_5V@#mSJ%=%i3r{LKvK&Avj5J zcXvr}cN+*E+&z#P+!EY_4Hn!z48h&qWngf3IPpO( zx~sa1OC3-J+hlL;81x%7Pe(b$6qj-~-v<5Q&2HL{dsjB^x|I^#XQwdQ$@9xtwnV$! zxwb?-rEvst$;W!8EQ8^_;f(!K295c7+7eC?rPkK;pwwCNrU$P(+f59r=)^G%XNx#n z-{!E;mcZG?etH zW7J#jU-$BbN5dsQiG41ufPjnr%QNL;M32z4%uMZBkH?lqkdUrBr?B?^LPc~X#>wXK znxs;i;`cG@Z3&X+pEH#TY-t! zfBON&Q#L1HIaRQ&O(bh*hq2NsEDbBEupRt^We zDG$rLnSyLoC;Yzv$c}@#hcH|$6D1>qh;W2FY8&tD*j&iqP20SgC~xZL@5w~H1B}~U z?tFtieD|j9rqBuac%aBfbJZb1{DV}dF3|lE*MNYPlkr!NkA1sGNTef&fl(ZG=KYEJ z65eAXHimROI3+{2CYXDb51a zXECuD_QI2~MscBTdZEdi!c$4C7w~7HuSxNam*$rJ^|yMH1H2d8d2E*x>pxN^dY3bs z-9W;7)2)Ka8Uo98UGL3t+oK{;(^pqmsk~{$-HykRL^Mfn&>vQRKGX@y2vZK1|8mUe_~sjR?39Rs8@TdZlD z5F>Gf;(!V;KfZT1KURC5g-F*FunX3vgbK~KuBS6(7@llBj+67xPGLM*c;_o@pl)>5 z)x%XOB+L-D3_v2#8i(C|e|9+2bl_K?yNh7rYr=B7X7{;6Yfrn5&O!3BUSD}b^eBt8 zN+IGx$@fZkdm~?v!gXh|%w2v^+`%@P^A~ztO5D3JjPrU`?v)WVenX-^>+py#XAqi3G*RT3ciNlO>! z>&&8K6*qI7=V_-abU1hAL^3`nbyR^9b4Sb5ZJhU zXSd)+dBAN~7tzjplC}#6zc9r11NGn^5*sMr^Ek|{i?|jpE4P9FfrtN)V3k6^tQ3Qv zJ++oK?kV$2keELn4v?We40h`28P}pw1W2fV1?)V=-iKP?KqMq1n(>iN;RftC1cZeB z%Vgs4A!E?hNiZ|bw406$^P}g2Z0HXJXqo0gAWZ~pEq3LUGo6hx&Y@D9G(p$!C?FC& zM-`VgGnDFdT%$qG;Zl&uZIuA@=`u&?lgIuJdjXqM=56lLE+WsaSmmpC(8z>8Y{FeZ z#x%Vd*ZmYOM0%zDp5m%zjNoXmgQt^jZeRqO=s4D_dR7& zN_{)3MW`0&$6^JrRW#(%nnJ-uRz{|1g+<8ttn=Pa1eO>hxT4l_vi$#5LQi0gUhNln9ASDjEew>H8*4t&&*Zn5Z6aR9Ur|W z;pEEand-+s&de#yJaqB@QY`;4*g#@y)G7cN9{LROkLuS*;H<9zb@0>M0pjiF&s+Q= ze8tEFT-6-VebO|9v74wB(f(3YEi_+aW5@*DtAx~RuZm_c(@_ZiGAVJc(@~OCnmoy0 z|9*IdZQ}Vud^BVzqchw4ia!J{9HaE9iQ)g~xc?aYuMNHtLtv5bSEdj;p_j1*IjXtc zO4)6W?tvJF8lF~tDTqm`-~cGK-%5PxU<>>c5hWbT=qp1{j>+JM3mwzEmjTyLiufWd0P}(F!K{vSd4zx;rb?WI&Q8iuU3^#AVp-!BjzfzY!bL;tTN{(lb;NJR&t;x%C-qUI($wqrqF&S*Rwm4^ zdC|a(fY7N;mm;-BTyQX90SNSdzSWZTV2Q^F4Rl7Jf~ZJVs>eef;--q$3DFL@Fzf&};iemypQ=f!Q^dBO(In zKj6i3f%MH+hx+uPBNv_D&Cn1fYoo9 z-Y+i{ef&;+-Vr+Dsv3CPwqCU?_oX}HB~B{vcHYFRtGJfS_R^QhqI%%&B4;+0+v;ST zfGbNHO@}g*|CHnhrkfbL^BoD&n)B=duWF^KIM?l&(_FW&m>^@+e{!V$4gOYOmdx5+9nxR)C21sqO%riH-ewRw-FE$usFPqPi;WzZtno#qo#kT3zfu7Aw#w^F zi~B~iRL{IAv>uq_vC{UEP|E*oH0}2nmXGT}ta0Ii4Vjd)x%!qLTEr?qZ(=J%zqiij z6e;d)!}Y!_)qR5~JS+rZK1Y`z{IYZ#RpT|_9v(_3O(s~Ay-r0H|J}(z(MnG+9fr=V z&y^p2pkH#gU4R;wF$|%MA}ff_*dSo`_k358Q-ULQYcQV?j*P4;%t{BvZ^;=gFQ41! zJ?zko1sBN?FpmDTy_*;_>#-V~Jt5Tgvvciu@=yJ9<@>{^w#^V#{h#6r=2Ip_Iw2Wv z(sZ#I$^3Kt_2D6A*K1taTH}8P1RNxyw}BGx6xN^}%~tDqE7gXQ+E9%;Ie+|@rfN>B z+}bK!ArT7=*60uXcEReb#acxQFUsJuPY+iVey4LYH@Jjllws&?3Jd~)16}<~9?KF` zR2^JvN%TrU{MxU{DJh@ciQYg3B%TIX^ON6BS$*))ALMFbhIY!2+tp*JUbJ1`-brk5 ze}Klj?Y0_?u7~hK%TQZ zhcu>X?9a7E^mE6jrf!c->@GJ4pR$M3vyysl%$C~i5o%O@MkSDd143%PVUoKhpoxQp z=yBu}NW3T9T!X>C%DW#-2pNwu5}DJARkV6TKx_tmC|Gpt(lCV%EC?I8B1Dmd_vd*2 zyLi}0X6qea%L)^>IBb%yoNKEhH2JzzCmSh)&nw8?MW<_vaau*~^tq2_`Q5s`S|y^W zs;k$E5r|FxX)rk#>b&JDTl}W@w=Rj3l+e z!E>^Uy|TVs1Z#wY{}2v(-1Kt`*=q|P1TQ-D(t=vd#C;F@gdpE1?02g)jV@FpD3$^0 zd_<1-uoeCq`%aps;sE0drjstSVzjHX_2>hD*B(v~#t5cCz-HvP-cn4Uij)1sQ$_}} zEP2bbHFGYneq%Vf{Q?A;dlM%b#{l>ypn$(+C+EX}_0T+Kv4W3+AI~KTd};$%!as}h zvP{7Bn2lMdW)dx{fFbMQ*z%WB7JFKg(c*W+?IuZ-o6NiDBVlC-vZW9wyGYHh?F$ym z;roK_M#hmhh|{SwlFngHUF0>LJgNQerHP~#<%No$qYSlV>6g7 za*C8`MskN`w72+xiR|BwO3Rz?JG~@a|6F;bTlOc2SzgNfxLO_3$QtC@EqD~SXzO%8 z-QJ4hf8{H{oVgCBnqd&3>+}pNd?~qr!{pW=4^Y^7H!tYMpzal(($gi<;NKvN(`GlD z!K;pw6+@1G%)mbcrE9q{X^SS?ENKdM30*UA)$c_{5&Y9bYPpzHJlchFsJU3p=ChK- zr2pv*t~F9kH=Qf){VZJ>$NC0 z@Cjz6GVH^dX0pU1D=2r;g5#5tNH6~g&5k0M<1-)IDzPWth#{N~g$cZqd6(lK5rQf& z2w|?C;x*{ceb=D*HLf=dwIzg*<9%?lJvqrD&Hbq$!l-c|>t3lu6UQ(k+8*Ky{OI~_ z1#c(jVR>ri)dG!DYO#>USedZHPex$>5+_Qw&`Aro?(Q7{x$d>$S4y|OXAak9YPGr(!IRmBw)kNrh=E6K(~-7gVfPJsB3%cukqi#Qv5-!UXXEKBHNobae=6WsRAVr` z--C9kQ9;Wvo7O5>@Ew+gH^vRM!mj_WrLqz~j{Gt#9sbt zYNiqmNMJ_Nc-6u#6nF9^xB!26h0D*b#tNCWN+#V0M`9OtuK?nu8YBYBWfA#@I3BHf zJR?Qi>c+M=b=rujHflO0kzgpG_%j)FPcoh?%+c(=TWG}gJK~k;-y>M#JC#BZeM?a^ z?*lt9GS(_0>6eNtLel2z?W9ZfxpQBj=*-@V$j?f=15|?h=s{k3zm1UCT-07S0^u_e z8)1VR@F>p-GkMM7HOY{pafhkB;^bK=9d;ip31R;I(w@nOt3?QcA|=j>jV(;m&Yp`!yTtfiwKFJ5?Guo#?F|h^)U7fgF2UX z0*YUTT^7fq@WZT%w1=D>?qazAo*|obf&C_G?>W%q9JWIDTKec~tQYSRn@TCv_9fm4 zgw33SBtx)vL*;W+JPZr;(GH>7mZQm--!F7dQW;Q6Rptt1JB(c9-UT{DvIEEqu6>8@ zFt_IQ4-ow>G@;M>*)C1oRgp;#TMk}vYbgTdM6oH;XFI#rGQoVIjv2=DP>bSkZz%Nc zd$ECNBVs~NqpTA(bRj}k$6TPT1UBRtT+c)++wNa17Fv*x8EKVhW9UdF^{>8HsfX~e zxZuETbHVf#lJt)&gHh?8T{q>E3M?F?c#xWl-EM<2wg=)@gEK<*q?s~&G^c9f`(x|a{mz_P z!Y49!5W_)gTTVh<_O{3CJqpC#dTno%h#wX!3L|sG_xloM91dXm&A?8V5&YV8EGzx* zD>!OF?EX(^rw)Mbmt_Pk7YOro+?~K&^Y3Hcr0 z8a8CsQtJuJoL&y*Oe>%qI*ArCFFIh4=V4`zspV#xh*=?J2zm!F`snWBCVb%LYv|C+ z@8*bU(*DG5=ryo4`RdQtj&EgZnL@{O-z-z=A#*GSEs?9@d5Y-jA#)r1wwFSj2W`y` z>Ojm|w)_RQE`sVuli?xFYzcmXb-S*9?nE~8t8S=>H#MP=Ya5#SawHk)iy&O*s*VMm zfqBZ+!E|nAFnH4Zux+8nUdc|s-f@2AR|%LtgRwt@52RuWkhiXy7|_;BCZ* zsC3}xt-VG$YCw48lj!Is0GErwF<5?e^3dLfY+A$5NySU&g3? zb#2%8bMxO(UM#LiV725R-cCEoa&2;+WUDefqP+yR{Lb;A)atKnPDg!%pQJ%c-`Jes zlk{%~@TMR=Uklt9?Rq{x2zy{^!U8Km0A;`gbaH2;=e6@2YsrVTtK~ASi%m!5%eEk$ zAb-F6A{cM0-)o2?B`J1~^dYm45>pFuOrI6#kiyaSLUw;hskfs1ZgMLdle2|o`dQh7JXksTzB|=jOhH*7j81eLOGagtU6W= zP}LDBuU9&>Wr_W~1p3uYxR(_NAz3tI{+IjCmfkZ6QGyu zyj5#f=kwr}9#CvROpA~xFHW8JlEX9;T!QhtqoLdGF8u8vde2UAPzaf+@))zehstT- zxA=~A>XwOPU*``Bjc-@#mpOE{c+!ps@DoVnvSR!%XFI7(oBauQEMt~st2w|$V$qs@ zq#i4`Itz728eYeP-na9~eiI9B*PZxS5r9kt^do$@_o4ZRFI|`F9urq$e>thg21w!r zJB`*pOdT=%dvyIhrRgmDQw2?3Ea;OI$e3d&t|uPrX4A%$0$6D?6|Rc61<-so5f|QD zR24N~ztLylX#vD_S*#aL32|?X3|ps~F&xTKrSZx4EtMkmkp`_v0$TXb2*;|nN+!3P zk6NU`w~{&xpn~z>FgJyY&cQyQF!0ye!Orcq%*Rp$bJCCZtTF5_)9%Ut&My_M)1vOc zW-h4>aWq4WC5PW_iUe6ZNmIOwg=$POB<#W&?-)9UNWSC7E$}Y!sW`cEySq}j++u+c z1yvYsmqxZWtv`Nb3#n<*&A@vp2T0IkJC;qB*lDHkPv3NvLzwaf86#z4vo8kJ*4E@O zg6KAf%Nl;w<5uqneG_5bF96ds*{YhOhHRx-g5-Y@t7nW74xT3mC1E&!5z}{A(%OC%s7@l$vc1W! zSu4kdxvG-sY2kM}H>0%1t{K_~iAr#6TK|&CdV@(tU_;pMgnYv*^(4I3@1hxl`Og%C z|B)U)&J@cgfP58GzO*IH1knR<5#&xm@y5ZykUnXSG60>o9-Bko@n#J zy0z?}4T*CO>G#IB2!-k*(QcfTNqWmHlhLWYaC^95(8 z4#CxX<_*`sYWwIimCfbrYw!?xN`z>!*WeMB2i8Hm2d z(t8WI6vsw@uMvMp%%c&the=X@%<1>aT|F3vCgdUir#*G`3e#ru+|+))1#WIJUb z%o}T~qVTtxmeS)TS}hy+6I9T?RosjHc^oYe%?-iIdn=f9vV{e#U!#VHhik+=eDoE$ zJu5FoHNMNEz7+q+u{jh-)q~@Vx%GK|;JF)zaux(p0i>k?-~>cpAICl@$Q8jR|r&0}}VB>!=YI=gtp2p5BDfhGB8Cclq=opUuM z4itZW!r{`4c!R){w3~GpbH@MPL$qWxeD*K~p~%=x100nV6d+kN3f;vcIW$6V$BZM> zzU`ZbRtDK(RX+23__EK-AB=EmUPNP#J*RrN#tmS{w==y@f#SodER(TN!`$91y=;@Fv4y9-GTk2M{o4ch*?`!UR-LgUepbBB6{*i$(V3Zf<%{!UEa9U4UQOboV)9>`t00aS^sv@hr15IR(!cC0 zqYx(E&HV38-n>-=5g@PzCTMA>G8t2oh?Zuj`n#}I;NojKd}w@0h3VUfVaz}`_2lum zoL)?DpZ5f%(iEariK1Q(X8AF0l%kr$oEQ{)Od|UxDooe0dZ!gDxDP zTKG(TL8=Px993 zT$o*?UWDV#kGr$`gie*HFEkCy#cnKWBidt%f`-mM>)ltxm?_@@n=g}3-6cpzni}4bFke z+#o72GlH4Ez1jSPFnm&oLMw~!5+``OId1K}+pdIymS;N*erE2)Saq1`+9v2eZ%tLv(?hVPyqd#KsxNzmyAz&ojd_TEo_vj0|&;hGN zjOOe0UKYeW?teQA;w`G5>BurmzC*+#KfPlcxJ3>#BPKqdgRQ)TsQ!H{o`??-?q)fD zu9B>5tW0+}N1_b)MB@&`Ye2-}K*s?9C%y zW%1NadRb|k6g_tuiVShH&S%3NrBXa-#6{m|?C1Zx_#dSxYA9PmR?U{qjodRY5F7CW ztR_ISEYDZCf%KOcu9y)cATc4AQEfLAAmF|K#z*Nx@v(m!sPlYlbG3A$Rp0U-th`YZ zG|4dE{wpe_@tYn*xtt56#TqVc0O~65dlr_&L}w$O&0Ksuq`#x1{(qlifXHC^?HI51 z_fEmTeItZ}1MGi6Imh(32lU@m1I`ZNo?i__$CVfA^Uf$Ht;uM zIt*;2`2_#F=bvLE@>x!wedPHcl6n3`X8&si|DIk`^*WS^SOU{lYJBnH(_G!BKpZ}z zmu%#5>dGpna@~!5EdyrLg(^R&%srqDzAB)kOc31jYIwUe8u$ zcFjMAbfg|_u)sH5DtiTl3LnhUH$7E%DJ_^w1JW6hMlil8)aJ@pvbm-E8&@S+l*J0+^S5zse{sxenh_qp&o*L>o_LZivsb{2#@3m=!T>hVhKNx4sza zxs`s#VG9HmnSfbB^u96d@k)DHxnkMFkP|-KGsKtqsXWS1p@f%~NJW51tN!+kdv~QS z)or{;R(brGUH+GwE)Ejx$M1<5o}7mM*IGoB06%EZXn@wIW!6>xo7pMMBKw42{!9W3 zF^F5Cq-(zKRB*cYZ@QU8@AQkc?27#_#2DVD(o^vKRh*XmU5vN@UFqY8a%DpJz2})O&GpS-C+aM_2q>;Jp;9douJbxm*LIo=cJ24C<6hs zNWh1Mbg1`SR|AbjE8hR%7_FpHkrZjKqux?)59Ynr%zLYH5=%K1Robk+bAr9CV+bEF ze!JvhsE(+P33#|#2{`Y#-r};n5wX3yX>qiTnV&2Ym)^slK9tKYf0~o&v+gP7&_?eH zf2n#^rC4OtXM?Z%>pg)v&%FP&fmVah_M0A$G z!4J)ooDL24_i}a%EcdE@{EXfDFs0^`;8OQ^*ZU~vDS^TkvBeOsKAdcFM``S(6g=fxwi}A0h6qIrOBVrz2?S$v zA}rb2oB2e5ub4m1nGp6lKmYKo!*d)(TPFxxDrw*_O@q>hM3p+(s5P570S#U2fkf0r zP|-Lg7UoAb*+_F?f9QEgmA;C;Ql1xySB6Z#lrmdk<;}M(S9^On99APN+47DnZPDMu zUNs@}&3wO&g@dWU(#iPBw70k(pSBr~=$pyL4SNOxZQ345GG!$QbC#b1t-6}?ucQ?c zs^SEP6;-m!-pkUOo4D`f%{b)haqwkMgQSV6XqI3?2&J5OI_|r1DE`Yx;`BQy{l;DuLXLj59bN47_;<8c$9U=&+ejy5xTI2k z0@Hb`t2yUAi)8V`USJ_zdvH3d_j+gJWQ#wt_tr8N1FM$R3FVfi<{MCvUV_|M z^|!Yr@>SkFK<}fO3nSx9ku5#dl~b%3a6z4~Vo{20>#{ZjArsOFwymt)Tg{v}(jP3z zwANHDkK6&Ye!a^s$WvvzI^S;Veib)`RK#&kZ)Wh0bCX@`l#=$my*lE#O_IF!GDDC+ zl6~z0fabTl#SPvm)ak7|CZ1YEo9W;iev5}(_??kna{tGVKQE42cPF2{jGUKrCDJZ_sqQpQ_L7rE&^+;YAJ|USRk?=WR2A|{Cpm*|& z;o+JadluIFbL<#E6ZJy^-!HC+@5UtB99fV8Sf5pCX+`0TYVs zZm?{;wjXz0uQ!3UibqFGX6L1ZU_U}j$fYSnmZiC2EUrsm51l6S?7cpih_B99vPSu* z)`eYC2th7gE&>f?U-(RALt2daFC`fIsdbv>c{R6c*ht_Yqib7UJ-hZB#?q@387nr<+B2S*a4XDudZ-PHwd{|( z>9@R&wa^e~X@IX(VSaMeh3VMLaHSrlQZuBlTjEWRcEsngCNaR=Cj z?*iAlJAxa@U~@B{nV)R4Hg{8;Z4Cc`MV{})xUpNWyI`0}1Amvk-j?V3_G-a9=t{1h zhnr96^BXFmsh7hURiQoHE`X_~QcoMLqfBMm4jps}%)?79CIF#ZH&rC2-M;C(< zbF1$6g?=AUI*j1j@0|xkJ4S4GjMcAFOIzQ3KbWk+hxGtTe-G&Mx2(z(>sK+jMYeF|r9M`_F=zYwK(MDCr7#mLWt zh3lbPp&E*1xcuu6l2(EtZ@c`|>s$-HFERXB23|m?)|`wxZIMSPHb1A;nb4xYK)EKK zNT=*K+b>&4d`Lgpe>*r5-|a>^(jr;ZW6d9Pk`i$>nkjYEvQOREPO-Qw=VrUs{rUEM zq$TZGrcfT#0$REq^9}NdX58pOy4bjr(AOnjD3ue;^plTA0j4hJwRxOe8~XimqhniS ztpp|%s-4iu4GgaOY(8UFT$0=MN?xdx3d5bO)mIK*^W{jJ`}VoJJz%0pJ+sxyi34$( zPXsSHLqK3)q~R2^l5kuB^K8EJQxT@U$URwggQ$H|%kw9488vIdKbQNW67th0enVzF zQ2`#UmG`98%jdK>ZqB%nQU4PT^P;u5kz&b4bVKbbJ+}`=U}Ndudoxba z#rpxqI)2-hAMr)?JI%~)-fR^DL>-jK_$7!>?sk6XAiLg5UA70{K+4uXENNuEFUhDz z?*+Sk-Xrv>bdD)oJX0$ng{ecl&};pzZa4&hJ`jq9<`A`M{>NdN51<3a1?s&N6Yf9g zC`l|3Jd~xU?cJuf)1uK`)2$`Y8GpABHLH!-cg!1Pe2d`64?}5^1)Nn+c17T#%^MfZ zP{|CB66bOe{H0b}pFfJZa)=6c{)eEVihj*&UwwKY`b`6+1=&(-1eUW1GYF+d-}+F$ z&xU*Fy^B!87gXaBJ$Ms!4ACu?41e?spKnzx|IHigMM3gMR|jTiYc#tvmFhN&XfJ)4 zu3@jQjHbnM8D?KQq%@n4%FU4uvf6$OC?rFd>iQ;;QJl%=m%eOp$8jLgG*Lv#s7hsS z<@q36q}K|48}-e0`QuWK&scFM<4!K-QQ^9XCTl@S3AV;SmctE* z@Vy}JlBmiPyAA#n$ba#M`GG(*!RX8FD@|rqMc#xBkM3PF*Q1;T&5Y31@pJzYRU5CZ z>@60V;9) zRUG7Xe2`*8Ou=0e519$^(1?R-t<51zL2lPZqRxln_r1LPkN=z`!@PAGQws1{JE8=)I5fgi^hkzY4rQIS=)^G=)zT(JTq}zUEm&?e_8X#&0vhCI>fe9cMf!+_ax>fUWYr-xA&W`_O;{y1Kfq!QNb3 zaH>K3Q55kxA#7d3i0RntLL}s;EfJ*OC9_gh*)9KvrOV1^vY|>)-<7^`vJ6hM0%y<2 zSfqNQM}{s+T)A0*t}P+*=bK9-r^&$94ii9#`@R%p=HQk=L0D%)uGo+|)&1=Ay{doP z6l=;u<7wxkE*I>$>5E?|qbm6DmpeU}0JE=-c}qjUugb%QN7YuU6?h-XEGO%!)~->> zX4}4tqOEpSHN0^%bs(y8A10}lA;vUm&KkJq@YaUA)}_b&t>8AE?b4FO$$IwrxeM#b zwOgbv;00^@F_)~bGjni*ZO?*p_Z?W7**6&cZmjAAW} z5%=T(uN?AqhW!%bnX#CObmZK(3GV=n2W(>wyy@%B8;N7>5oBwe2MY&#St~P{N2-Kr zy1)HS?ldolp6dr3O($vucZI(54Or&&u3T`Z@{RcwkhE}ciOD!#HwL>)?`khxb!;lJ zq4@B7elH97+}+?_R4%gC0sW76z;NS4jwVS-k#)f1Fmy-Zuci3M=$`TJF3&vWCmmTU ztG>>T?`3sfT$fQ88cIG`t`k@iZAiHluHgKdwB}bna;&E^G{8=Rk1r)I-nDNySocT~ z^UeXe=ZoM1nh>h`HI@Cz^qo!l3nr!g+Y_G>WYBm-qkF}Na~+almiI&EF&W0R^AYrP zOp)-Fr;3DvP>;F!S;p6{>Br!sstW!nmY2E((nS{(n@R=LeqiOIf zLmz@64;0H40JlZ7Dy#B*7!F0WN zyVArbrRzBV*^?+e|K=`P4Tz_%x5v_6jfflb(p%n0Q@Lr4g=5u}uJ>=G3Onn<3_J~6 z>mM;|$tbW5&tG=DKzB#Ljb|1{?Jv<+Q!Rnsj#Enl04>2-%-$>yQ|z_wJu zSN=+!^u4Y}G+FX^mHi;Mhc$ctewznFq6d-@LdX4(hYReN<)>L=uUvcz>~m${++&_X zkP@YBJD09+-g`hbP%a;dMe-WUlmO-^4uIs6bd43orH^qIeBI>m^Y%O*%tLkTh<-bhf@dCxc!}8iN6eyj>u-go*-XhTc0jm zhSSd)$A1~%$e~C)W{e=Ql5e-CmO=NrY&ETdYk&j*ql_kFyc#ueznhg9lkLQYY&k83 zfDEcohhBk9d>1N=ZGUUmNZB7=C*=1ok^*{@vFXxmwi!fP(&^KA{;G&vs{=Qjhs-T= zRYEd=Tt_A?#@vhA8i*FRhZ8xSe*}3^pjPzBQ!#wA&+|>4aH|`sPbjJS-ZfpH(U0=( zrNL!o@#1}aQPz4g`JV#&PGENvoUF{X2M(_^9%hB?A=FQ2g?_uY)1O6eYcG3Q;Ktdd zHZS}hnE5O4jE_B=YL2#=1mhDEc>_i&iDsitt@F$g6&-qZFjstmiF`64WKrvr#8g=h&y>KM=dd-m*{+AhP_v?W`9LMH4Tb)zdrz*y{k{Ornl8n zy*CcfkJpy4LPsgD6SZLo5f3JhnCm0f_Nw{)wHAw?HJ11Oi0?n{ z(f%7-ewy`l1*)+m;KX&`Cg`=^o@~6XF(WT9u`2$8PWXYY_3RNa-^{yQUd;%?l!j`~(Nipjo*gYnSs_URH>p1(fUPmX04_x7CR_|X7 zpTa1Y9^vx;glbaH+Mt@yI(Pqqp$AxNl6Eue;7noCWQ@(y7%{G7{v`X8l^?R|g^bOV z+*+Ren`CU8Gl&*n;-a=>!dz*)ZsgaY%g-H&P z`!%zJeOM47p}|%%n#F4iE*~CE27QTI`|V`-K<3A*^b4u^mOVCMUK^48%7&MFOH?wg z*-={$DAC2ASeb0o?#Bq^NXN6cUHRLx+FCKi6@pp?EWV??E6`f>wPNqiU^8D~MkH3d zw$aq2#jz!jh(OpbrnTj*w|@vvTPK^&aK3}R2GQE3cCDa$Oe8Lal%o243E>M3yG1?X zr%6!&7CYY{x}%^AYjUFKK5Jep`V~eW85nkCT-H)|E>$c--ga@_!688@a_2Pte(|@F z8^7^hqkD7_@pM@J`M`a{rNZE<{M2I=H;x^ zM`2+e$)Imm<0x3-m{*vG6wOqTZ=ppUG=iDZNk951u3Vys{VVZ>a9=O;|-E zmo|hNg)GiL%E>957k421GVgow*_JruyRy;}eN(u6E>RN(d&yNt-!+HDcYU08-3m+D z;R17#Do=xX_Tau%gBIl818@mnJ zqp>mJ=Aogv#M(C-TFF%h-xo;L1&r}hhdw3uV^XLm&vU`w$*oi_2}Im>Qb2GuON!h8 zVZE3T#p+qXE$t6jT}$o-%Tj=&`Vis%)b__{hH7;6s4Txer`Bua@F^y0@%Zszch^kz zP@u)NgnF`E`VH&BgJ0EhRL)!8>{$|f)xDDdMa}G&rBPM?J<7os^75V#G71xLs?|wv ziU|7VhgZ;Ku=u@T8SgL zyw3o7gZ!yAR6P?3Q{}4E6J+T2{oBl3dE1JU+)cEwNCBxBrdH*HV79>A`AE$H?21*8 zfr{C@U#?L6sesgCgSv2HEa^Pxdju*6Ug26mx`%y0>>zVE8DalrMC`@_RD6p6;&l4;JCTRnPZ zvz$VI`qVp6yCatNsbYEC-_(i`z6d?JgntbQ$TjQXEcL5AW)A^N(h0Ui`5|cdj5@`~ zSTH@Ikb76bm|^+jMhzxagj`ht)?k8coVvNum?)Q(i>`j`+_czzco?WyNC8U^ zih_knd3st;FvX(pcRFZ~vNF-JE14hpwrY6A++VwkA{3f>a%@OF0AuD)<_dcDK&&hp z&(6sT^K;xi1m=5Pr*S4w2AkXIfutU@e2nk3iBL8dh=9cmFn2ia)c?cRdxx{#xBcVS z)m9Z%T189It*F)7iY{7vMC_Qg5~D_J(yCNTjn<02DkO*%A%c_|wfBsewfBk<9dp*bT{F5U&@>%b3uGjfG&(AM87pKo^`d`zp7t=j#pAlU8cP{{rkyQ(h&+QvN zG-dWXz9^+SON~=VwWyK&Jqh9KzSpjvN)U>&E?xHVptM`8K zuMn{~zi)iaz0<;5#pI2h^pQ(N>qS2un+G0u6j(A2d08#Bn3@7|=Yaj{hnOd(4|c^j3gIZ(s)eLOOWUSwV4-b$TxJ7nqVXK=uwzj{6j~j?`^w*I58IyAz$4*IGs<+ zU5ze3T|t>F>=h2IH>*3hvt{OM^3j;c*8Ks(=`Co8u#I94w!@hC^Fc_3R(tsnYBk>? z$lUiRkNw19YdXJ`shL+w+j*XEC9AFuyxFd5&!SvIJSc2L1mD+~{TRi8j7{j<)=tjN zcYg&Wy1x@7DV{*&ss)#4|LQn{|8d2>E5oj+#(8!o_y@cj)?pslR*f!$s<#>6Em6%a7LvlkiAN)^C zwSRjFDUdUG8aj0#)A@M!$1!VeaLrcA(ilpCEcWK6zgkWoX+7fm{(eEl#pCyx@C3YS z*+N@kx~$~omYACL(x>ORD^{73;t={S5?7M9C&ZuKv9btDI1bZnqHe8GvNWAu1$%-_?>S>x=)|2Ti&M30F&9gPhS() zJFYk%&1Q3LZ~wf?F(&zU&tlJ>UEd`RPVB8AHg`q*l{B4sHx7)5qL7)@mA#Ae%Dt5F zJDmB&fkqa1fkX}u%w3=BKiZlvl5>~mcm8{Y(n35Xjg1tlt_B`zc*9?6JAoCG=;}Y! z$upSiYt5htQF*olK=X&Vy26x%VKK+VICUy(lv58ae)xm$nj4E<=J49cwAiV zuyn^LrY+&S3$kcB*L2qJ8Iz%ZBwvQK*HKsJ6yi`=4%bT4M%?kD#Z0lGPw-vXu5-ZTVCus0!OfXmmRkRzq-qe%H-d6Z$ zX|{r1Y7`yHaGlRPW~qwO!xjN1}>2Dvd~0wv5vYvS1c(T4oH>5Wpfwt zy>xsP*)6{wkr5J7pd)fp-(BBku}SQUk|%nAkuOWGNM!9^1KM#S+sAaYp&x19b9#rND!Wrb!nf-sx*1X*VrjbVA(Mf5TU$5Rp3xV3Vn zUNmveWO_|$ttKKfywEj7@x=uRe;LKg z$cHaoINNo;&fSIW;RY5nxo`L)#}C~ZI?R@QGXG*uj5&gJOf;s!jbpwHM^YzaatT)A zrjY`H+W8T@ikRQ_w;M)N9un^#Y|}P<4OkRxg680vPI#3z`!>dFx%HNC(Oh?YF;zN9 zEmsyc3b$L|Qop_niO=ji%4)Jbs{e?a4=vA@iVzeCO*vta2~WabFB`Q1@->t9XsD>5 zeI{YG>It!_K+)VB2C@3v_nj6}=H>Gn+ZX+HZ11kko4qi-aON{5w)a?I2+1>WjaEvB z30M=jbR551dYYMnL>PZ9lXKQx(LUnfE zn0yg%pb^;i9jpZ1I6|9}r;jRZb&Re_1};M3jWJ@thfmBHj4R_O!;VZgEQ3ejsN2t; z>2Fsp3zX`zwY8}Mv4NFrNLIaWVB{knKh~)W%81gsSm3RHxPhJ5|FEJK0-c;LD##qU zq&YFUEy@mL2Aa}1&dV}x$CoLwT+gU`^X9SjZXQ?6X_b&z>8qn2wm&(>>)8w=VTcJ< z`PeG`C6&JE+%40=mod|WbZpI4*6#Z&J}7e*zVNL75ZZOl zBZg$_V>KX3ehR-(ZQoPaFN3~LHeG)Pi1GM)X^jDKt!QaT;Homsa$9$f&Wh3w9s zcf0gWZAxv7Pf9R9O16Bti-s8qll`JqwA?Pq#smCoKW?#E3GydJI&TdAycUvZBNeLv z{MyBGUR2R%+p(=u%qDe(U$b8VKvwKwZ1!8W%1Ww>uqam#Kh!_}ZRx zj1h1|?@&YK)t|b)t9XH&2d}$*BOdoM4h@dE07!}s_$OoSts>h6gc#igNgHtY3zlM; z#uh~Lzpz%Xko({XoFv(On^jFy6Og5EZLRkMF?+Y#=Z;3B$Lnf)S&6iY^SX@F{YgLb zAKZNqvZT_V+nk{A!&;x6%luT=B>G80tNR*K?lN@5zqQ;b#K!U2FPf{%z@)o31bYeH zjnU-OZD!;IWar(F^%9KW{heZnc^KZMPkGSBOHtqZNn0f$KEuEb+(TNiCxO)k*&NHi z{&z@nzF}EGt4)77R1y-r_F=QlaWohto2K@R6L~bn_8>|?UdFq=|BiI|-M68d7L~~= z0mOQNi`SoP-#W)$aFYSRVy2s(RJ!+mX()7C7K@}We>&h9@Vi0`{={IW(WbTX4BjGT zr*8J@l@(gY${{ZInMAfVTDPD~fjfu!OWa;JvO*^kXR*uCin|W4Cl1l=sbcUk?;*t` zh1hh!3-f{icSF-lsn=hAPGMT!EJ{(Rs2(OS3-V12d{isl_=<2(i7IPfK0abc>J8g0 zIkNBqeva4(ZlBpk1k*2bic)rd!7hj50yq2U3~Fc=Sy;oDI{guytoB3hO&` zHzkwitFDO83Z*~w)fmyi?;_6Z9kR%rG-(@G#Cx5HrA2#an7&?o4c=*I0s}(?G?o9e z#ooUZiA*}fIeo;LPl#)mKoUp1T8FEwxs&hRmxZNu)YQ<76A6ge8#?C3_2I6WN6|`X zh^Mw%mqzYX`1($jcDedMCO@}Akvjm+!IXXFnUyDf=wY~^`NwgW&CAbzG0>9T7fw!V z8!P%sH0D*$1l|KpZll0-i+KV@Fc5t&klaK7vw|Zm;B+G%o=y^jazt@ zG_O}K>F!L;fbIUu`imT==`%pwoTyodC>AG@YUkxh}$feM>`Q)O${{jzs z5xQRetXXy?-SH0#X>9eUe$U6f38c*?wY|e7Av{4qojEiR`H-@<7Dn_R#sp8SNoZn>w6&T;FQ8*a_xw!`6$|_QUVLV2@jN7XyR1R+T1e5bteXIDJ?+&%HY%nDhuj zFu}q0+vrzm%1-j6T%7@*Z#ks2OV6XGfQjD?<-KF%&97laLNGuU5?~e9y2c~F#JHNX zI77Aul4BM>v&=7P{(azLQS^k|L%MbKRK}ZM6SPGB&37ryqCWPL>&KQueks_q=B|{| z&KimN8UGwg#L!o=foi zyZ)3l(z3Pd&7#|0`0@Tn?X~wm$pOk|t8lq9qe=%oW^oCfD=I9s@OE6KFZNMfN@2IJZqAi-ovAUElQJ;xJlbqS)FamS(k zFW_!?V4s7r$zaVY`1I0O&YsBy5pW+#9)?W4ZXB?G`Dxb>*YnvIYW8ipb7)~$Yw(8; zDBo~{6)4-QWGPH8?Im(89 z2hfGWR5J1y9-|?70rJfg3_@uEv7K+^(x}gFjAU#w73Cd&v+K$Vgvk&KEOCU>bfV)# zdgqy(;oDDMbrPjCKlK7zu1G%C(W+>VzXl8r#0#W`c0k?WEKxq{nn4a#s>T-fBTITGgKc= z>)@sT`fAO<^ox79bJ8?2b(0mpJHNu|9CS1GXFj)IG90*U?Fee+@^d}r)*|0%G*@XH zAzi@j|2V@{XA63H^5pS{sGOwSNQ4U})*l6{ruy#I>^U0X&L1>imnZr`<68ea$7MQpq;77a31SJIxyia)!1 zxn9;-#Z5+{x`o9xIvk+4PZah`Nmr4AsJ}Z zQ6v0YUFBlH8kLLWpm_zf>;5qI>x;{g)Or)|3f2bI-|7CFl7g)sG5EawEn+qr%-X$rUi|5NZmGWpFiJ!@{mH7&+%4GE z=MkOewNcaPSG}|}4a(W3c|Dqc{pk~`Vr30`-1L}padWMiimH_O@OaT!LG$J@b*DT+ zpRG28`JynF#`<6lXf<>rAm$r~&gkMp*WdeWp$o6=;(ysP7V;atOKq_UbR6YCbD2bn zM1o(6Td@O8iT8!SJJNe_JUfz|Fi)W5M5b055l-YB&j^F6yJ;hfwy^Pf?u5nZB0a%h7E+&%Ssm2!C$1p7jyu%t-3&9s)WP z^)!Jhq%jp0c|J-}IdhhWm`tDWFzf6QOtUyVOzoDKFmAc2(w$HPK2>$1(v*Yn3_!--Txh65jFU%$W<( z{4Fl5O-WmuR{Q{f!^AqV2X)8Mp!~}*Lu_K2OJTxqMOx{bA-B8DzvA7dGKctD2rY5J z54hC#`7Hg7W&C6eEm`SE>CNx3x@*-ObH#D5AzdJ_S&DHc#h%7azVZZ(oxQ7(D&rZA z3p|je@yWA#cSW)m))Y`L2f*MVXN#zu*m8*H<3G)u|4~gNlY4GU1JPPk*84{_Ts<5T zuHt0I_-2zZY10ga2?k0XQwJ2;0bk6fkp7)&q#)IIXm6)ku{jy}b7|}qjW`ROeM~vk zoT-4#V$<=xwa@vh$|+)Hzf8tGJf&-jmsBC-+6|wd^ZJ*BVt*z>f_iWGvqM1J?|%&5 z>=nP|;QWNJVOYI6K&$RPXk>rqEtRT1y>^Q3$ztzX0Y_RuuPce7q@iLL`?sw_X|sy>Ku8W+hhL-qReA|MpK_BXJ6YfPMLH6_a& zN(H6yYp=sLbaPi}%!P-kGE*z?dHuR5_Eh<1@?E8*hCtrSywAOgG@bJ6tUJP2GPhuq zwKMG~q4k-4VkLp_f=QW4Shzp+a1f(kc4)<(}GKt#!pj=So^nHMjKz@aOVC4n`J6mAnQE}U%FGt?NqPjk<>!R)Ucq` z`3>-+vIOcrgpIT`*^u zfc6~gZ%9!1D)2w-j6F*S76YRt6{mhzI(`K3#W{Iv8*{iLir0`34X*#N*JQ`pZ~uxOaCo*jH~%)% zExnb$aEiJ^LFlUhb#{$HC`dNG(11)IX*$8--%TA*zRCReIo79t{rlq=$9rowjT-4? zkUyje;)dyQ*-J~_iVc)JEvr26>nZW$Tpn7|Q@XG3iL5I3haQHj3MF6zisa}I8c*fd z_$Yl_B`3b=D*U4Ynp%5Z>8V>GfeNSL3xXaBhB8q1nZAOL+(Y+9x0sL^()Ov-`ci5@ z`2g^R2#(oQ>g5FQ-jj0xzII;Ad{9NmtA3?Xbd{Ukb8fL^XMgXe(j~ zp{zECb_7vLR>Dx)U-`0zqdYobzwI8?_;{4vgHAyCx`v}E?do;^iAnw`$llg~L>1K*>1Y#0kG=SK6~x_8^+ByI600dY9@X!@rvQTK35c2 z6=dbDEC`MBAnT$8Qv4441SsMD?fGy^LGq>x#?`O19IZ0})s~t7O4lR~p*=bP`ngMa zGvK}AQViog>;PuDr^0M6@I!*DJd2gNTwVW^q&%qwN~xH2(Wp^6KGN`ke0^e2VASJs z^wUCf;4U~laO-Q}L1V6Cp=n*L3ngDOU1}gL;Ajdvp0M`(@CY6Ls9azAKyn=?w6_;` z%MVl1=c&LCIn;o!2?65{)+c7q3AF?Z)L4KJ=01A#N?JhAz^XxZnT5wO1p{ck!A_6$ z)#2)@^un+n?ldmDaI*J0#iaw3v)Ew|4Rf~p^<9}VodDP_6ni*Z^-H0aQae~?BA;Jb zZY^7I*H|pa*WbWtrD{jM0USHd@i9-RttcU~(t@cq32e{9X@y0d*%@*X;*Zz6-A zH}IETJ0fimiQAJTbwL$;M+Y(dr(2WW_@ti5vd26w zWH?KH0)es5!W=dtg&8)8xC-)JJ@!9vi%@e}^^=!#-Q3`F8vNlsBbXpT%nL>-m;nuc zW=EALk@w2=*xl=wijT7{BC(yp`yrNs#)cp7wIx(^RviCaU;83$Q3n4&4+m9r7h08K z&Xz|P$WH_%SOhI?Ym<6=NI?yH=;w5hfGw2U`_w86GjX@9jv&~({79-z?_MJ&eWXl- z{S9-J!YvN#Mdr5V(>HzQLE}<{Rx7SE7jC}$>)#*I!e3k&(D-W7P%+-6 z9jVde+{=uc!}wS0KNH!psO#)TM)&=P zf`0nfdndM=h*sR=Z(S!Z2}1%srnq;k2 z%MP5EaZU*m$3)l9 zQDo!_gV}t6Jq&}|mqy;lsO>ZXMw_3HIWfO|EQ=&VN652DWKfrAbL#P#mSEJ%1cbu+ zZyWN*ns|Bt9gG64Fz)*&oEnk0cAeq*?v*E@LR)12U{t_oqYT7_H|k>SGRp=+_RUb& zH&Bpd+k}OE@#bW`P4y4)nxv5dd#}%$o=OW*knFK*N-&LUGyE>^FIt%Sd2$OapDB8; z)L!biy}f4l=qHfVVbd!DeONMLCt_k#6rfP}ba_%!-oVnEXy;uZW%}YKjP@``5k_bJ z^9f6}-&qP$J5^g1z&e><$>Xy2lhzT;yPaR8^+dSsST*Pvc*6-#add!2a`KE2*LyY= z7srIuLGi`bj}ghP-j$o1dJ(E|JDM6VE#;ZscDUGE8bm$)c;+17dT@!%zXk4(=)Hme zRi|%|@^Qy2%Or7Wuu9F8xUp|*<2-;of~}6{G$|UOUkW6b9C9WTyI@BBQ3H=9Rmn%01i&$y+A^?P;>S1xk|f= zbYX}}#?3QvNs<-4!2FRW_BQ7MEQ-#UZvEMRTljyiPUT;9{g?RdKm4b1_#ZX#U*CSL z2|N`m_fPYVn`=_KhOQ=L$0KqAvlQ*WVaWcd?_@oa0yMp_uOk;k6tal{C{0Mqmq$T zBgfZ{0ofa3E&hKUdGpVTF(MjT&H+B(u2%VfrtTlZe?*f-wZ3pmLCpsoFaECs^)AqE z{!TSTLoDtAt;6O&F7uy1Z$75&R*9NkW@KxEfFm|H zIHt`U1*2RF!J~Ac<=EC)_2xmJslX3TZAHW;^X+s0*ZIS#bQh4lPpH0hfO2$f`Kei@ zHb(NY#_VkY92*6r*AYajE{_w|t>-G4t;oLQE{KIWDvY2+Rx!H^oo*C9hr zd(NfrtLcI!N()y_YmgBZ`1-Z|1AbDat@^mDrN|k1-3&K!D`gLIsTdvCHoB3{F5}eC(M@8Qtyk2X;^`HSFBq*3jrzexLUOjX6O8ik3MVz8vYUWxfoDLJENV3K~o)kq-y1Sn<9Y1NRdsT2cHGE67;jMDPD2FS~Zu` zGg;Nppn+|@3YSpsVnQy09)IlcqVSDJIcMs@dnxU!uS^*@$B&k3D{6Ta+t&Oyn}?4; z2QA^JGEHbh0Z#d~n4)!}5Z@(gnq57*0)?!0&AcK#=RA1>Ak@Rb%>y0yI(fTB zck0i5-GeoAq~d3xfNFp@LCiM}s`E#*yzwP_}wv;c|bTkd2(5De51|$Wvrb$gNhA$aJ)EAv? zrLQt5bjdNh_Skwv&vYK$umwpxyo)K)L+K3`f|*UL+&)CO4ZIoASLI+vVka1vt`4IF zs&zkQ|4@swXuOB$Eg29t?Ucj6g=XddMTKM7l}lfkO59C)jIN=o8? z$@x=D|3Atbm24;mZ5v4~Fius&`5oUUEKRk$_U7Q32xHcX=S6QJ^eO@kBR&_|=~TE@ zU>#k_IIrEkc?KJ&-&OEzEBeOz>u*2{+T1jtyu&|%$Hf36ZGSX z&$qNO6scHG5ZRhhOf?~vphoAzmA9SanV7C5bE`D?4HYsY;aO?ZfP6bWGzF0xc>b2!$9u1#@j4?sa( zOOnpo6-5zkhQ+onr^u>ZSdGtn;R(bc=cHWIS#Uq@lFOG2KarQZ>`~}wtSg@g0su2uzH}cmNIdCJCE`^(EvD-e4^xbaGJ$RbnsR9< zh`~th0N6y^TNo8nn+}>CeRtsDG=Llf!QhtQjN~GbOFt`3=cT}Q_EJvMLepB1=$QmT z#OP3}dW7uYkJLS37ymL4U9T9Y$|VmQgL@DY^C8mF?()jx`JOruJsJyHz`#-Yh9kL5 z^8`Vp^N^`gXnpDU+EIRu7^I4&Yp3>7T>c16RXJ_+HX_|hzexgH7R$aCID$~<;VTs* zt*hRm6&<)ZbY+)SrxbuE7njquBHjrVt*929|5$a*UH#^3(Xf2LvgB54%5|Ju`0p^> zD&CNuboy=_*OI#DT6i+o2UkxQv{x&3Tdsw%v}1ws(e7fEJ&*j_4~`)A3PKp>SZ=T^ zo8oHM!EI0uS05TS{AoAxt_Z0jJ(V762|i3=AM6JXFD`>-Tvi9NK9b*_D4zI8yNp_w z=O<`i*O1Hu^>`l)<+<4AuQcVD)Pg^Bq!4@4{OnVNJ?HP201Nd8F|vg4i9vNfjbdhQ zX%Yi{ITqtTP%a6P9<3AzKlUB@-RW#iz!h<~b=AO%xrf|05cm^>Kh)77M?J3Cu`Gc! z;~Jq^r3rT#N+)%wRc)4DA+c7ZMV&wXCNo0R8Z*>4HYHu%0b8WMC28P_tl(8% zkxF@RNl&FYOHfDyGuRQ_vJrbyZ6(#bS5p)0eJU8 zG?0n=F(vo8s^{WZPoYf;p(T+z2QwF1)4e|*zg`7h-ZClpH8;zyu`6F4$J2j{Uu?m9 z-CMv_g>$6yP4R8A*I|CXM6V{bfr8~u$g`N43))I`NlAaU*5rjnWzELpcna_YxAftD zW>Wf?kv1d#d7tJXiL}aNK1cOA#i~+zf>So-v2TOwVk2!QYv~VngR0-Ak)@&EU>I0` z$3%zk$}$;ne>6cPL(Pd@J-*am-t%+BK&*^KGs_(FB3IyKz;&3_)Ri06ltyyWr{=<$&mkU$0KA>`euy6 zK1%)_aHUy~v9AA4A3q5nk(`)9jkp2!u3CQaHz_VVl=Om!Z~(n`9GM<2yr#Lv12kgEL)742;xm!IQ347SUy-tSiuG8gLs;*=9rHl10fT{Yg3dskSx7`R_ z>#t_63A>XU?n}WH7|Uoci`X_Cv8{k5EsyPZ$0_Bg37SN@F4eB?aML$wj~y-zz;;Vo z+1u`>CdlY!HZTlTb4%lIk}e88_|(Nb?_Ku7pEmYdF2%M>OM?#zL?|*N&|RMnkVS*} zvg!q-ohU7Z+yGgr#KcM|3{EQ@rxh>QYxhbqYRmw_?M<8RmjhAHDMp(1WVi`qoR1Q~;c%^Zwc>A*9&RV9QID$t2n`#+`3>>L7+pGDs24ul8icGHj$noB<|DB~`erf-!a9t^Hbp60I<G$7Po*71F0V<(xXW(%zAos0Qm0W6gpJI;TwQ1W7Xj8E!}J0 zLPSe+hT<(mFU9kS5z65@Y>BQ08;j=#KWA6t#CJO;M;c#1@_Iar3huZw2HhVVGxkzD zR>Aciq)8r8jea4nX&kq~^)eY%mksDl?r|C{?!SPWEO6;i*-a{g>LE4SL*yVAiw`l8 zQ^9dY>(GTH(1-E@fuFH0qBXUU)M1dU{_2?s!#|T zQeFq)%v)>0MorB(W6&IUBm{b@-v?#q9Bua%3jA@^e7QvRxRvtD;?iEs*sRSq=w4^T zqI$qR?d1C+8E>Wc2P8hmH}!_$a$V$G1@TMhZo#T~^u<;oMAa$sMS*#tvyx8UZ^8Hd zSDzP}@||XX1B$%FZ@BWo2dK@#P5{@3dD;?)Z(V3fZl|5D@BpYTHD1!@)@0c+tkh5` z=^AN>RineWayL}*a#c|v0o^c0wHjurS-8;zcvC2tJCr_pB)N#?NIScexFj7jC+0D$OAQ#p zOKxF;{Wce6g8Nc0U}Dg9c%^9MKIV~TGZ$B#e)Qz!e;@(xr-lC*H0R_&uT@u359!-# z>^hQSzIx)$0cg!G%H_LgZAMySv2Ks!M~D+#2|aF3 z>u^4gXOT_&x&+UD77U~`#bP{PsF)wrsC^;#&qP3@n$GL7vL?BtDUp3GU~Er-_ddsW zcwWq5eR!AKM(uqFx&mtf?h!|3QPIb~!@$LmwLLjQ6bDnZzV7$G_(#o2_AtIQIA$(u zyVGU3|4<6N#1B$hdzFj@lR6p}+^2jm*wEz33crCsY5Q3CT%3_ygqqjsTLom*^`GJe zbKJf%;;C|;_T~izH=Alh6tNryLl`3lyEtp2wY#re`|ASpz3F$IA=ZStFoIAt&rrR zW2y|`X2y@{$Yy%dUiO?{y@|*Ld>h@(B@Qi3_J@oYpA%N}4TYhvwaWX$*CQ%8C(>E% z4w8>VPiC%hI;>cQgK`G&uoE#U(GfPd%B~qFmF&b;Qjumvtw;dIaDh$10cvP_$9 z=&8EWXy}CIUUEPFbZ^~{AaVh6IOMIU*ig%zxCgVT*_%{7Nm7+Y^q!>bhVD7Rm(eft*K%HXxY?la~lRcl_qbu|qb5z9G8+`uh0A~0w(Ja*! z_2fZekPH+aCz5I`1KoI5vyxJ#T@fc_I09Su?Y8Uaq<&N_1r(PBquQbzZOCJym;trQ z&Fl5onEOCON>Q+#vJNk4Y$*zE`AnJ!n|BF%Wh!w^^&+yL2hns^PqO(lM*WdDiy#Ix zUJ%E_cU}I0%iJJm&+gv!EoiCoG?I2&<_ig#yhWEvNPD`@?xPVn{g86~hsR^JY4a4| z>TkYUNuZ1}NfT55`>C|n;G>~cqlwECuk1&J$bN^639g{>uruEr`d9|q;vX#X7m}`6 zu(N_hp0HSH(136FCc{t^I;GPSsMRyroO&umhGtN@T4u;b=1ewaAK*=A{7LYajDY9m zF=s^Rp1OJ|{Sf72MqWjr^OeeJZH*Z+!_y~E@b%8xHU=nEn6R9ZnNpH>X8xq!EC35< z)uW=0nQ81A1+i6oC5#aww^<4QRDH%D_}1~wXPPq_w!K%?Q1U;@8REhd3+@!zW=kLBR$@kx zot>z;hk&XONsqYeTrmPzs`-tt?hjLoHRPh;BX$=!RK8eP!hFSQsS>NpKVgdP>-7)} zwZeX!Ovw&q(q6O7>rQ_oSf^ z8CO0C084=RW}P$@O|wMtO@kfpcP`pPJ~N;VGrnfyl4gQ{!L9N~<7 zx~$K+Q3xWg5@sd?xkgTRm)8uH#n{(n<|6 zm9%%!P@uf^ByjJ8l)YA`Xv5=s8@s5|Hot}(2)EGMp3!?bzuG=u)pCmA zzVljhMzu!V-fnD7LfArFs#DuqjJ-akThnxmo6H11%b44*2mE+d0 zK61SEr3Gh+7NfaMYB&1SMI_A(Bf7jty4VyJ&ZM$%I8h(chwG$z=I0|uo1u-l-5Xaq zdE-6Etbzf$;pYKC^@A`q0&BJ{TutOg`(nICj6dPs=Cz@L)X@=No97k5WY5-2k!yjLU2pm|npUT8eqP?geCIyQoVtl$My@J#HF>)w z3~l*#2rWoulQ(N9UzyUz_FsG!KF9K+xwFiacbwy5e!c4ZZ%Iu%2a_a6N?HCe(5SQ< zx}2ceHuz@>dT8#j4f;knj{ejqubc)Rcq?DyVG%k92u~;~^Elmke@rP5@zN-=J1NWr zuXYeXVzYvYG!wmW2t4lVEwmVTkqPqfGCVbz5diHKiT?auDUM9z+lZ+-n!;h|%Da2$ zCCVV3Ytsotas($kdP(Az86RUk=5#M1C~+eyNak@j-CHo1{c<`cOsFW{Q7q2MLR6-t zCN<~{H}U$awnT>iu~KtlRYd$#y%w=s%=@Xg`YuKd~Z9LGj9d>Q%7d*ci=yDRn-<`9hQi^=WyII1D$jp@bw6 zKLj;xjwO*l2t<@;t6iBGOK4J)?(B9l963xO9PrgxrIkIcJB>IGNOXE^(vubFckfno zH!r-{@^=HAl1n8ces0r{>^Ed> zmF5w(m2aZw0HYN|#cLDgC>*CCslPCzijziafe4c^N8eVLufHlU-`4)M_(&2cmLpCO zsXmx7BlMouvK?Ooo&(?=5dX|5J}tTW;G3n|BViMXvgM+h-qtF?F#hzQx{numIOx58 zoKQ#T=2LBj3<^Bs^jLCbzu+ypR8jU>tgPaw+t4^9@MmMYW2G(o$*AU-?iP=cn znkYAaZxN!=2;E<^i?swN2cHL^g5mlua0YX1=OJdfQr!&rMbPiLw?~ijz2)=ba!OD5 z5o#cD_Am7|w3suq{|~I;M#L5f79cUk?BjMzgW{fxuf^iz5qi7n=)VTM_ACWQBDRO& z`XSo1j>-2)4>dNj$cD=8_i|TOh5TA$MG|*Dtp!?z1Rezjct?F^x}s6TeTU`*SU<^p1Scg@k&TvQE{8>pSN1aFjd#-j?gcRDYRXxhss1!rAQ<&)wgK=@;M~jf{cR@X z-Qj9u0F?|CNf9)a5=j|_5qSsKgKKdC2aJ4b9qB}8*1h0XdjO3@^|i2Y$Q*iNrcSfX^bNBF}q{|woAv_*^ zlvU?|z3t$A5lKrk4y=uq#q6mL(q*A@-h5XP+s$5{cB!fuPCLD-EEZQ_DzWO3XRT@= z&aQAjLLjF#4cCDt-6C_FmKEAK78;e;byhFdEM(!+)*0*ck$>(O$JD0znnOe2?v1yn zs}Z7d%+-`Ir_S7ntEw-m2L=H0xhC zGsQXK*K1u*g5k=+Nt$*2?9I6HK#+IPh=KUM1x^BGXG-lFiGMUx_uAg8HX1<2rL^QD z9ogTi$-UY>MQW1jDP6?T91PGnHiu`uX|P8;Z3SI%g`|w(T8fouz7SSh;WFY*(VyoH z`u6+^)IP!Rzf6F<971m)YeRW{tKVv(5w2IN)RQ`|PChW85dj5$sTnlpdZ0Yy>Lcz@ zh8S#)1H0^?%9}Pc_bM*E>P0{%q_fieXM%7E z4G!ccRR_~TI-_xDOrDBZ9JwyGiPTdm59VV|a@nid&xY}y=(6|A@1;#z*vZ`=%!Vn9 zaSi3KoZjEXAFdX+evcaA(qM1;!&XGc>NUoW=6%$h$pQwve0{SX-x;np{ z=J@e&T0g0gMEF5FFZ#R8c@mw_ut&_uN4u(HE|2F=i8X|S7RCox_3uLHc6+M4JjHe= zD5-^@9KjPYTJ-u$qDc=6hrH~gDVQX0(swthH;dK^yM?rj;fYy(e$(G;W=hjdW=u5y zoEf3i%$ukv+rAaW0v&5kiZ8M(8!9YUe>JP1)keAW|FQR-QBAGux@)OeKtQD_2#87x z9fiQ%_PXc% zJY(!J@*|AFocZQE-|wx@^M0r@S$aAZI1}1$tySO0|4d(Rzn^iv{P?Y6ev+&9vsuIBAV{EE_f zC92=}pKaP^+I`iUZ1t=;#h(_~)`(Sjbr!nM;-o6%c;nwy1fTh8=iA`N@W%JLO!g8~OX|4Q%I6@|9_MA8usjb4=y-p8@K<}r z!Fl~ydH+3Gr>k=3Vg^L)TkFF8_>feZ)Cd3K=hS>jDirxyF>GNB<8Gt+v#r`=Mjp7G z&pi5N>ugTyjusI9yhF-AMNHE)=iH_% zWMO}CDxn_#koys0VY{k0S7!O6H9xEw_DNzMF=A^tikJZuySJins(YK3h6{@>3>vA> zFmGtL)Zg3%I`_sqXIo(fG;HXSqe8N=AU;ErylL_S_YJmVQC=uS4HbLaHK#`Uj@;z> z`u;xg^OI-rzQBss^}*h}`8>zRd=O?5VeZYcFo14`-gZ2U9(&;=?z$~AtojlFk zX^f0YoZK*2^aMDRZlbf)vg(Odn49a;(|Pti_t!R7z$@DsEiBbz58ojSj~QDE+1V86 zAAGR!sOrNLUs%zQqo6cdwtn$>Xd6gz$zHA=j$)dA4fzAx?8`sEh-!7THLSK7V?6hC zED%FV@5#V)`Vm*N3v}$c^k03Uowi`3fp|NNqKB17bxUYZl^i0B&|k=0&fQmW(}&MR zaqPCEnDPePS*2M`r0vA;=t&QLWK#HYZ^OA)aDk(=E=LC~`EP=E2%JvL2y9Ue<4}RR z-L@^WatTnK53LKA;_MP80EI(0J9uXy0_ft^GgPj)XpX%Mt|(E#ka|#hgmL=fiERhU z)7ydW3wKi1sxIms#ywgpD9fI?V+&*ll~^Gz?X;QhbujOchT?UxAz6+ZE7XK}2K5qr1?xaGv0jjbVaeKacRa5S9J_>n%V zMhS}HKo#rA1@(ByyTcAf4CZ{VQYHZ~7Blnum2b4FHm*Q~ri&V;V2bwY{-K{}*Xel$ zMK^LUq;gI9faPXcO*OQ5E0HHk1!AGOF7B*^7q5fcJ!+^WOF;R89#wm5tW-XP0a(`YZ zaN$Zss%@oYVl90I|0MMD)|_;B`GB5mXi?qr{2(FxSKo=2+A6gb@3j%0Y1%03dbGSl zH^bn-SN-u}8JT{ZV|EQ@cEJAJ(>cfQk%``SrkQNa!cdET3F_W_*SQa$a3238cTR6O z19E*4+6@Kw1vgqywOS{ zkzvw%KbR@YX~X7Z_sj_7gEGT#6wKl?aQ0d-n>q!>q=|Wdf%sxYJ>W{4o zE@XNbPm2Gu$l(`Ct|3$LCzO^|?}T~Dt^8QXdC_oZOiiztjljDaSzs!!oTps^&G&f> zJC(p=t09WCO4i;UTK3LBqY0S|s-sYD9HGL9m+9!`IXCTolb^0_!0OlH4j#ee@mBTr1CN`D6#sKpXsqI~Rqspathr}A39z`je5bgSXeLBMU zV#dgg)q#9VlT9k{8mg?||AEZ6z3tOZ%WVu_ehJCwlc@~q6+h6b2of+(W#X#pU_gfE$YsYM_1;vU^KTQ$v{aD~UY@Z%zSjr^Hd2I_tW5&{*TKhqcEHh@0fa^rWL6l!ULxH{`a+>>!_ys7)NjoOfFmD zsZbbM_SWnWplMlqpm>cZS;Br3P@nCs;M1LxGtskeko@4)3u^YK$9wLhDV1ZW??;`4 z)0M7>8Ie&DX?jBTJ>}nIp6^nToAN5=d945fc8)wrcFzxA?_e04UskM=;g;aKu~^!PwnO0hz$rmL^bMJAEv2`R;P}qfl@$y%st>RUtcoIlM)?!$*hh2=vKrJ;zFoJ172X4!WyH(IWSm_4fwi zU+ihj`8E%$j)n4GQeAvBk1ck*p(!l5 z{5vd-y5*?THc8yn;HjVcZav<2$Yp+r$8GvG2uPm+s){ZREDLJg5PrvYWK7Isoz1r2 z2n6NJUG1c$0Ud4JkUj8epGmSG+;D}TxViscFmX+`;r*&Y5ywHfX2HaU#R08eiJ4xU z{Yt*!yb=tpKfc)04iAj#A$t52B)utChPHmd&fDc?>hH5IA9Wn#cO#RxPq<2eY|xtY zSz@&Sg>ChQ8O;mjgz7Hbrs`5%&4eJy)_8xLb`wml+>OOFa68L>_e76NGO?KPJK9b(R1M-exH~qwvoM)j!~6 zD9n@jzBLg*j;T#8qA@aqB9Q(O{~0@StBjlv6O(^9sSWL(jghe z6D6FWQ$DjiZ`TsgVl06ed61$yun1-xmiV@c>gH+hO*eVZMPm<1EP^R<@@0 zYh~4g08Cqdn8HwL{@L`cYp4wI&DN=e{gbYh3hPx7%6=~ic_Xb`zkawgv7(F$FVyAc z{di>P9CdDI$gTU)mWk@dlY7KV?v+OPb)UI6xsJ~PxP&=a`tkQ)VFugQ9|7hq+5jJ* zJF1533M2*&HZ-2P!-x^gIYK1o=&X+P`57iIrL~k88mWSj>IH)4_U{>@Y^FLEtO<^9 zoEHkrTNIEIsHcjwS*P_W)kXj!pup*fv=~0N-0Guo;CC#|;_@Mo{i<2rlkyJp_4td4 zN2e#Ao7eSf?d&IrzWCXK0@vJI!ZriLE{s0BYdBrhxyP5k9;LSe1ZCJB_O-JV2N^ul=lN_lcEj zbkgr)>m(nUW*chjFOmIE?o|TFZA8=;hzvh0+eVbA)nMNkO{>{I1b6sQ{JvD{p&uXB z&3|QM{F8C(0-my#RED31)-msZf;u5hM3c2!j_lfJgZ4 z<({BGJZYz-qYh=Betns8LgfI$-`q96>%m_rEdYxR0G5B@1v)M_Ah!*(VQsDuQw~{!2o|*y*KT zes>8VI4!zyjRVxhZCf>tEXR<)+++7X>I0UfS`m{AT=zz7s2mwwx!pN)hPO%*^f1R9 z2>kv}g85ge-;@7khqzdD_}$FnF1znP?LMX7KrR6Tll-g`ZB74@xMFnhi;l9B3^8dOvR5R0<)jnSYyIk z{zX&&>Ce~GzxZk0E~(t{A>jhU{@;~+R*8512UiW~rPc5MHfc&(%ACzxOgxTI{qH^} zv~lQS++GpD9h&?QmEe4b4}=vaKV4t_HzJvX--n39w_^#pcmE* zPq8gLV9z-jpWQbv_id-32LDyJ{^%p00hedcdr43>Mq>#z`)?)fr-QnM%{LT?){D+b z{I*_91T)&dQn6RBei@5e3ZO^4IQQ2Ib?@UnK(l{~#IrU+L3dDpY5P~M0gAh11`xYs zif{kb?|+m5^o2xe&MYAJ+0DQ9CkemqxdL=k*r-EU{@K&~vyeD}N7DHEP=zgCfqGWx zFMmDZ*KRlX?<~_OjvS?NmonotLYd{_TiBsCZ??sM+XMVbD!9rIr| z(>+Dt<(BMzE7JRm&HCzz6JR_+2+QdCeg|LE9%cICfV ziogB&A9L|PHy1{g6ev(MpM&iPrh5R=j4%g%J?esfSh)G*GyetOCFw5 zX4l%KBkVGg?2FTjCfl8IU|ua&rDKp>WGBw$5kj1Y?-b@4)QP#b=CV%=WOc4p(;4>e zoXR;#WinU|71c{K@8O5{Cewudf|L`)B(X21cIb!mbV6yn^Iy&fRUl8jJtVtI(%4nQ z;0{h?bc1x`uIpx0PVaBADveqqagg@?YPURE&3a$aBvF?N9(aYs_v%4{lE@8h zg3snw{&@CsIEQOh5pKM~W&z~0c%*fj?}(_d8#gg+Q+~(GbWmGQejh3yW%-^WR7{;_0obq#Z&#+APuV4SQM z$$ArA4)$vZlYEaXSS2I0^u6n2c=SdLkbCw?4MypPi=QtZRPv^X#UozKoHwvX*7G&I zU)q=Kwi(GmT$~|_^!D9v*jrv4eO>3n2Db~UVVTB}7$yljsE9S>urn=Um!I5d+Y=q4sjvmXg>iKZVCTa_|3a=g7u~mn?%I>7f zZ{7H+0|WQv#=1GwO5g=FouOAJFbpTbb_OH%6udl1817l@N?9uWwCrRySWY!vm~yKB z`g;~#TGEKyT>$Vk#L2$zWc)%|+IsWjtgz?uCcjU%BvW5$Fl9&m5u&K3-bLoD=+UTw z-~09AHYRt%#fZR{I(wdTTk^yr9qET^efhLKVX5aLHu-*gd-o)}hgC#2IBffCbIBR^ zGs&G<0(I4Wmo~u<>D)f4aIE2Oki^9qI@ia5U;|8kbJhBzzMnR@x&CClfct}A)0*t3 zs`{Dy@g!R5B?6->K&t_`utN-MWV@g2Ra^ubW!OnjqXyGV62?cuMNaNDu&#H0c^P%u zUDz7NFlK;AMeO#ZN{*7;zqmb~l#!64JS)QnrU{+%RoHyjOtIS-dOz^Umnva@)}(hQwm<+MMeC z%E-9)IZOfDKLJ-?Aib&hyRfXQ(${cE@wJjTbhY*-%RFuTiYf@+LI}Dj`6vjWlyDF>!mP zg60a7Ne+}BR-X%8dY2?@Uvh;R391N;QW0rQ6tcH_055Ne`(zWU5CfWMIXymJNcG-d zFo6gG5M1#+Yw2sV@?wq9$IDQds;JhAP6HCo9BCojGK8U5V)WUa9YMK~;TR_?-vqVx zz(v5u0qDCl`mCBcY-Fe=AJU!gxQ|WC3+FQ#hYT6`mQG%&Lbc)HBLb)5KYa4vztzM& zDZYw=^1!gJ*8Yn_r^(p#&cc8Fu0>cYRkg&$#I(w4fc44fc(#odZa= zT5kR5Dys=%Bpzh~oZ_(dR!Tkrt8=^->fl?c@0HT4FaFUapp~U&IiSABHIwo}X6VW> z7Qc-|4#-AGQITC+fESy{jVk}ttFYRNT-H13R+=7Ca#wEUSvJ_6@?h4=9Ui$%e%9zg zIgYWn#hE8IeTa}g8t_P|R2@|rDYunp*37x3IBHgcX&b8hf;F{UYpp^5#H5&Sh(W<| z(p8A>Bl$WEldkQNtob-E%W;4eYX9R(T9v@4cg>F0WOZ!W0+;4kcADRMWghsgxzJex zMhg~g4QH6LBvjKG?5uKsFNaKr6}ZPwBGz?iCclsRTmNV^1Y= zv{VPH-0%TPI0mtE`bR1(9#;6vu(th`CBr592dc1FXhFR6=7aM)GY&Y!NTb_q&R3Z7 zD9&QEZuukg^Rf8i`O`8rfxWS)-br=&yC(Fw>@Rs3-4f4SKWQ`Ve4<3d=7>M3a{=E7XtVj)VqPXKbR>iZQhuMsj zyU5!PeED>K9iv;(g9l;(eAuMgw%mu*V;zR|#P+9N)A&e)aL`NLkm>CvC0jjvBG zg~;(|{br8Ji`5VcM#M|6v_8;XM_dYu<&L)*&q!dzExpjIHOrMepIbFnx1B}@?m10c z1)>;Y@hYmyrq}D{u->FR-of(8%A<^pN#2IF3xEo- z>yx2D<7)xxu$^3U?{%wM^x)KO6S3GL-Y}Bvif{Hi)!&x}RF$?Pr1Flt(5xFo%4Ycq zk5Bm9B*4)D(3{z`Y0t`v;)^-WC)>yJnzM(n{x7{MNS?RME0-YH zdYb(}r*;7D_V_Fgn|Ynbro?g91iUz&WE`8~gw7jr{r!}k*G}^)t^MqC-<-P=KR;j0 zuOnNMIS9}#ys+8$CV0H)X6A{WEPockAaJGMzH`015?88Iy7WyL(q{{M9gl&RyJDyU zpDk7QzTIcvDrM*iTLe4>v0etYs(csB5A^2n;_`4WJW<$&Mye3~eE>&7l3{Z~zI9#a z9Q+LT`!R3)5A1~U35#OS%}u_)$H4z6W;|iL(83n)eKcITe&Ha`VtNg_&_TAbXlS)^ z8FIXLCTf|6caVr+1UJGodqUL=7u@;gBXD>hbp!reQiwdWTTZ#{U`MbV zHk}zE6?FPyziKqqUWrE228~P<+@q6@4aBleeY_!d6gVZFa2b+bBU*622flJBP!7_< zn90|mFT=E!pO>^QX^_nZq;@`O*c??J=1mbF`(4tYMpu6XE^I}#!REvs87K5wk*M86m1# z)0z0}ZwR`*am~$4Q@_114|q;-*A)&iO_B(k{?DqN`;x2RGnezsRkTyzf%M^59oTbH z(5@f9+45CG@}IcmxwOC8N>X5&99F3r#V&q{5>s5dD&$W}nOV;HOY) z^psl|XCIWr0654Z%SFg#&%u7#^<%6=R<<95+W8uumJ=oS$O#A~xW5 z{rBnY>S}q$1{8)`Vi792N!bK=%q!hpR7Pvicq!c0lJ^)Tx;&0b>%`~s_`cNnTFu`rJfXKO+t;z;BC#U>F$$JeWNVDUv*aKpf*=O z3bQtpmD2RdNm_Zcv`1{-%zqFzoi11NWU%-1U99X04tOn}<~bA0F-wjYB%HYPZt}r|G8nc1%)-`$NRfx?D9!jx zy|esNGb_O2tQd&1yii#~sZ5iAsP)zn%k1HMRQs|K*j8#!JJ&Xb(^_lq98I6O6*@ou z&(_%epoiKa8P8%^)7_jjuZZSZy@1Et!qVd|jJPJrbj(~?OLD#5<7e)(CO1R~}{Z@YNopwr^?SQiF49!3I8RqO1#1g>|`I|4&tZBd3~ zU>aGlk?z^2wzQq!Rgnbc&kIcpOZqA2_B=-L|mV(Wm=F#jd;srwu}^H3{$dM7uR# z-Yf9lLu<}3rUmeX6=&^Fh4NCn#i-*;RGuoC1nOve zAN-i_*Qrho-psrQE`L-IPs*TG9*5YEbDeV^~euCiqhyqFxS zf|B~k9A;VvR%`C;j2vgK-=iyWcVfxI6jDkZbBlF)0EaD;v1Sr!3zAQp{5-7lO8}03 z*u`u@omCD^W#e1A6LJCO+p7da;dAnSp>&rlo4Q~oKe1Wv?OT7+hP(%PZ9=DX?cS6l zj3yAo<|=DIbGzSgf}Kpz^^FGjlMvzqmaelfFe#*eVc?D`2i6z8L{BGY3g*107OGih zc1x+;c5)(IYWrguNW3fs;5{m=&Z{;IebFYH1twO=6y4Oj*FxpC`YTYZ`N2~Pyke+p zIa*4CM#DtWU5YBMeb?;@7!}_0dcud<7oWGVtsC6*s zqHe_!uZsuuq`Jje_U}(}@$FC32N{9#AJAi-#Ys}CS;jUw! zdIzI{qZ*-Nqu~|lPhOgq7Q&oyEmjKLrQ@JeC z;Gvj!Y}SW~K*B{>7Sr`MvS;z2;}@}c`g@TTNV za{rKRU1j&=e(Tw(aH4evQ3*+t67x8I;6ncsT^c8J_^;IeyfNVVaHQYT+j&x)7qU^} zY}VmLkBEoYjU$~#7t2#}b=JASrL>rRN{MZ8ro}($5!KW@DZY+^b~|nbE*+9GOj{ua zv2wbN(B;^TJcoMlbX9MmoC63wDM>=b07jItPlw^HTAmc##W&R3@J*i^2i|Kq$pg`KTZX812#_l1^ElZpN z7!f=hy$BR2~?v|?ii)JBQ*1|HC}fEQwo$9js?nHPXW-G&ajGW-BR*GYZ@B%fH$7>=?oq3 z!@+pve%iIiJg#B4Q<(kgGXa~^%=uPJZw@5qpDNK!%d2Z=3$Mz-KlYPmB|}BRaddm) zlM(j&i<1{m$JAy$n>Df zo7_=){|tO{_*A28K}&!J>#N{yYB+6DZa3bAG&V zd&{UOsnM{xddt(+z~@Q&u#QrxZ!h)LrZ;oRY8~nsOq%6CY?l_+=BE+xs?!>|KIlGI zQ#g2RI0GEqOg_&)qoQpPf!D~hOAxHT^~99QoL(vLlwotGt)8J`851xH0pNH+P5;+UddifzwNe z)L2f)gqyY3YiTx;Xi|`@k=jQi1vsH@dqUhPIC`a=>R_>ek9w`W#5qer>beF;G&;re zq+hB_;riVsrxib7?#a##^vMtG0+DwBZn9&cthA~TstMSW=cX??H|Y=0*pbLX_FDFF zPhI1L&D%{8&|zKUYO{z0*9E%P2a5U5gWi0#KkTHLv;d?Z+=Xp*ju$$gMT&ZSIjwar zv|@8S9C=`c6k+b)%;qQ@UY2_3gDL)<(rvniZPkD;Icd2*(2e6;-S=wFAJ>)PwTm!; z;T&T>@nN@xTtdD)laDaQM+w(rwiGTUa_Y1yVh9*n+;IL`tN9M@<0g)(9i-J<-uP#j zmM6y-T7=_oJ*^sN76GQ9Q@=>|?r=gnoqGEW^opjd-l3b)cQ!VJn9IA?COAR_rqmz@ zK1->7i48|-`QoKT%>8X#yft61&;#{0eCwy_89dOS{|0B&FYCM;bmr8?w1P$2E=J5IEE(>_Xipdcv`6>=EJtldYv9Wx z_fKf-Zl|Z$^WJjloQLNeo1(bDU>YuH5s*!dF*uMbl|av;n#y(KLpTG)&_xiEB#>`L zdQU=jiWyJ_FSh!4>%to#Odt^=wR7#xA#1ei{5i2T{3`BXrw7lbKC5B11Tdn5#2s*0 ze)q8ve&McAseRE!$6skTk9N^_1Mj_pv3$ocD;eNL&(m9DK|r-2!M5CXcy~5-UmoBu zDB+hRgQ#D?V~D;C_K0O$RLg zvLy!&?^FzWPxV>#4=`swvZmw%e~iw4zeLKemj8xx`eE&EhKnCfR6L+OWl9cU$2x>j z-U{2WOE7dZAfn;<#-HF3~<}y`{OysEDPJfs%r}iNE zSPOyysPcwXKXVYYyv1upBz2^IxJ*YNt%&8-LnMLi_op+g4P&cENk7VTni)otKqgYd z<={&zClc$3oK7AXzgwf1rEWPIW_K8oxt`>3C3yP9an9-jsa6;&Y0(C2oR;r*vZ7Ud zkM-?aq?Z=!><#BSIdD$HiMJYNrT^Nmsr`Plai`vxRhV%y#*i_o=ah`EZnj2q=p^70 zBwhS@o#DpL5?|RUZmlrxPH-aHYUm7))mcMdcPm&<=Hl+Fm)ifi@&oM%UQFR}Fv) zg(UIBR_e;ToFLZx{Uq;N|G`0!j}RZYU&AejTjFfpBpjT zZ+N?rParx92?dz2{dhl2H4*=RT%Aa;j5)R80ni%1jRucsS_{6o_f2MqIo|;2CLOJcaGw4 z6Um}GO(tHKAwv4`Spubzu!J>SX-%%49Eo1PEzh8>{AhYW+P+VD7(8l1lrayzHjwMM z`V+nPYgz{7%&q}RxuNe~!QMneuyq$}0xR;&HI>F}B*$R-$|XFS0CZQji2~kTsxvQF zN!Rabv5a7p^TxdPUnesVZCX?eGqASF;|cG_Z(@jh}#@`Q#WuMqjr^=&yhxtpB{$@T(!)#OQrS z7ztb7Ug$UmeLjifJ-LP6Rcm>NNbYq5*AG?;6ehKy`*rGl)@N(A^Mhu3`<39&&G5gw}l; zJ<#t~h>r|d%L6SdU_UF;_~h!+(V=2oe((PFGHSjF1iQY{S{fisl8X;|q%(N%dS4)= z*L?RBAkJj0w*RcYKnK(3g;#K6WH^S!sQ2-{ym}Q)T3T7Uyh?GfTn*OwfMfo`dl8dF zU6@)PMp5?T>G@j96;GPs!B+OV$kZDO94xyAS>T%=!|yT5Ju6}{AuK@7X{qIk?!iOu z4Ln_u^Cr{<-_UqLI2Cu{tr?$;O!|52#>}ko!P*LOGAU_k9Q+89EJe}x5bc)CcbFj!1iwsu~F61N7s>KgyIKMV$O-{@1{j@&v(2egc zr|ri1)A-Q9vZXdDcyz`dPi%TQGl4_-4~a)z$uAODNJB{`mBC=thbz{as;~Y3ZclDdUNX$XFJo znCQ=Lt>7ER%;Fw)w$bukVx^|BS3bSEH88}lHTTA|u!{Z0Z@>S^heP?o4U|HZ@CXQY zAzA*N;qyXV6>L!fG~7}==J)c|j&8A$PQAX)_U`6q;LyieQ5w!>A`RNZI^9!Ml$%ejXUxO>u zz8LKBR~@g`8DR<3IfGnUTYvBD^6k9#e5j&$JUIHRDGyhF6+1&@N&z{Kfq!bOorwSF z%MZ*G@z{56g9D{2{-qRs@5%Z-{FJ%>E|ov~LK)3Zqk+0yr+03;m#fmiA44%UlWl-g z4Fqj8I*Wg@uH@ihUq?<%_`CDPaE%Rq@u<*Nc)dwHC?&77L%KQCpYeKI&W-Baos43{Wf0ARp;-jZ4lvOCUaAfQB*lECh@7REj z)^GSt^4;~H!n}d4gOk#0(8{4=uNz^^6YuQo>^S)NB9PjSZ{3-yb1S5;2acSr=Q#BJ z=9NddoAAPGj`{kZS)sq#r8BNeOdQ4?(=#ckvUp6%dBZ$ma4*^xzjq3aANEe4>OnJf zTJ41`91M<}Mft33zI&v6^j)tuU%2kHY*zv=C>R+D$v?l~(b-h;bID$@a^88RfA$j! z{A!{`#c0EFn&WF*`ByiMK##tpsPn8mFcs;V)maK0d0SThAag#SD;e_# zzqh(zZa(6kr?p)ADY-Z|5d*FkAsdx3o}`%h{L+Q;j(fG`=qhS2s)@?siIwV*M-!-{ zr#*&vw6RA46(5tIo~#|VRA^~df^SN3NEoEkk>^W5H_WX;8j(`Z+#Hpn)X2owGh- zN0btml0bF)4&~sYU;3>%o7oj}K8bqmA`=$+ChE7j0sh!N(tl$q9UE?X{&<^9BrmEy zeG)Gyk6PYaPZaMjQ*~pa7N<1p>rDHGS9bPv6WzM67w3R>>echMAE|o9Z%!V?l?!|Q*E7B}4W0Tngz&d8AuvsOp*x0k zkIdIV4#BNWD~W zm#>s19E82B?$QwJeD@%4%^)W6sN(YvK^E#yv?}tZl!NV6^K45C3hd(SHVl2AK-Dld z2YKdV72au;24%Xap24$a$>*yTT$7(pl23+@ykTqXomx?aeJh7lHj;E2B~jLUMM@_@iL^sVyF z_(vW%m94}3u@|}HoX>5ox#)?HvvgD4a%*i#I(oWq*-lee*X+~MvwdbOa>5v}^6{MVWKB}>a3q@mvhbnBHN z?DU+A9Lz@S&XZ$mP$k|JiL{r}T`Cz}+^3uVr&z2;TdWHHu6>f|r#E%v=H^TN7}+~E zbsC3jZI?pcj|Q?p@P{X!@^tv&@jx`REL-W*IlgFfd1BLno>Agc?z#nZV`v}&IDm(> zC)bS&B<~$uFq8b8DwR`qp>v|Y{=uCxPkVH~2UzqM=*Nl+Y?$IBk<>@dM{cBn6 zu7j7THhT3_*H%Dv5H=)jw!tW7@yVx37pu-x*ThPt^Bss{nWXeoO7P&V;)psEY1CRM zxb}PZa1bWQVlcY;B??QOi@9eU!j3j*PeRpBtM^#*x=w%P9nLjY_UAMz{_3`8ai3?} zGxffS($3FzkG#>Zio7Zz;J`h)p7*XK#J<@T6oCI^y#)H>+WzaHisypi%3h7diyRh z@zGGV+zrX&7v?va1_GPzM_G?AhPv+RN4FQt6zTfIiIXD%zXLeQuKI_A^<#QW$Lx~o)*t(+eKI}zyFwdo4{N#S$q+`) z-`@Qe`V2xFcSkYpuD@!1K4e1LewU{Bw#ECbg)al0#I(WH4%>P-L6+s6=;|;R9YfP- zf7&Q5XB^(^c^7APaWpVAvp`GY@QnDh*qy?6~pft^o{!(_r%%uk9Sv(S;%amZlCWMlCm^6 zYo;i2I=(2o6B@OWebhSF2PSx`SMG~daDh>`LY;%$w{(TceFbz2EM{yi@Xy9Yihne=-tYSMa?(7362|2@5Jxo; z8Jwi@70U4J^%1v$fB0gdWL`n6IkT!|;#R+@4Ti0mLc)Du>P^|H8q(_N288E(D5`$2 zy{M+vVr%|1wVPq68Bu&JdsR9_68u@J_|T*SdA1``AoO6&@qBJu z2VBrV9id_v&O$qIJ+LZy3cwFk`jwrVy`8C`HpV-!VBGBC31jp2Ct8!s44*!;FeC8q;cLS&9Fu5rV=KppF1Oq!D z7tB@<6!PEp<%^P8oxhfCxj(SbAO6^^kE+ZP_j@AGB4_ws-s6vn$B(swy+yn~)ztOq zHES4$oHvQdG;x6Cx_H!nc&&$D&?X-rkM8uUbr@XVoDjcGKg-+S}WM zYPELU1YPx!(%^y0>~r=`U(br;9cW7d+Ph%a^d^W`R(e-o-{P*a)K1LAX#dD3q}^5$ z=<8VCX5RLUq6^1cUbY9Qr}A?1Kh58eQ%2TB&XD))G;!2#@40LHNFjI8sXWA(_%P^?GaH9l^uz?lxz(2xXLN11(l z^Vv(z>LH}s7cl~n&nraW=z{Sbu-?&jirm!=TdN0bt$lk*M>gXJ%$|^?5$x91`|2`1 zPaUV6#)|HgmQL67=xe4om*<&p?V@b7CqE(2d$bA^d%sf|z;&qg+f#k(wn^9_O+zDc z=6lD_o-KVWtSq1|iN2QRCmuwR(na^9$^ZzCM!)>j=PZF0&OMPz6lzFHc+tSudystW zX-1NPpkJ8$=&X!QEs+M2*p5n`a69weJT~M4}nV zgzZqy8a&6uy9HD3FV}H}kgMdY>WX!XK9Fq$4Vw~8T*Q5)t&Ec$r+u62#r#8`vC>{u ztIxiceX#T14lDN-#ZVjDG^g4^vud+_-R0l5(FAXI(%vO1>nq!4??)Y&a42=3XVe_# zua50ZrB_CNA8ttnU5@@Foh|X=8aW4hm5Z8`v;y4K4m5o$)maGSlb3wRlzMLWX!_k_ zTBjJN*kr$CUu%s)W^*lCl=f#E!wM=%xc!Ix8dfB;pX08w4iY_4y6q9UA3<5x1XcI< zr#T?%a4Bsi=89SmxQz6+N8r7L)ch?GVPnt83cF%_v?yBOkUJ5N{!RA(^^M%;u_kCY z{jhb@q0?MTfnV{r+@6$)!6KH6oT8$E^`}TsU|^tLAa^i5y}o%>a;JtdUSWXs7}u@} zH4c)!SmJSOa^*0}`~J<~c_Ma&vcz6%h$7Kt1+1e@yGa~^Zu4^J2pgla;Z3$>(z7f zrUW%@JbAe?@m6FD#@fW7o?Y1ECjF)lOV5^JqviWPWG{h%b@k{g3xz+@tfE$mrB(T+ zNrFj*aK*rbS0nwub8EB z802KRkWiBGGk)8#GAqR&|E;=j(+)4mhD&4(}%rl!4UI_VHr+HYf*T|KfQ4EoER-pNw9

q< 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

!K5~Zh>U~e2 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

  • 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ȹpKrQcS&1R&RB9+iwqABKdhky%m-2% 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

    &DGdfQ&J8;&twWOX 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 literal 0 HcmV?d00001 diff --git a/plugins/hls-semantic-tokens-plugin/SemanticHighlightingSample1.png b/plugins/hls-semantic-tokens-plugin/SemanticHighlightingSample1.png new file mode 100644 index 0000000000000000000000000000000000000000..33cdb171efe1235d89f092daa69b7c0be4907ef0 GIT binary patch literal 320582 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

    q< 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

    !K5~Zh>U~e2 z?87H6WoXp9ZrTVJ)dYpCPgkYcO-5(1s3;phAe7-3HhBV2QgY>gXo8-^sKB}903O#K zA>~W!jJ7*9DPA($gpl`-ApMYF&fauH?JCeTnlQrNLBr)#+D($l2!@^{1gwb7ZA zP{@%Jqjv8l_>>%{wes(KaLB zWI6|V2K#8J4;Wu7P7KIuJuD0Kb)i@}X8JeCb!^lx;4t6)CBUd$)vaDg|I$&v5PLB5 zJ#J^^fEu zLi5j9tFyb$M~RmL!mWb%3Dt!zS&hr@M_YrRa`_uO%+%*+&Yf(S?Uksyu=G$izh${0 zF@QdsFBBClDSyGn^yNcpkF@*vQOEBlEztjyssw`5HVQJ*ON+fqIigY*|W zscikI8)~tmKP7%HO4%*D5%Rl6n`af!BS!G7W=BH2n2g#8|M{Um(}11#9y{CqEd3(_ z%2uPM9~bV=YrDQ$%p(q=HsZt>&Z-K531PXB?<`TA!2tfwx3{`v*JqQLZ?&il>(8;O z@)Y`L4-?9Up5YnU%Ny)LH^ntI*9}}(2)I~&&1R@o&6T+4DrDNeS?%XN{$LR!Zhfmq z{shxy?Edur(|eLHx7K-e%p}0uIiSfsy)+5_EAvqaE02hw)(17v30x~d$vxOVLj?Va zH_bkBPHqeJoUw{$1Z>D*Y-?Ucm?kAElq85y3Kr$i6N_SxwtFDp;ezmo`_S|QQsYf` z6c_oqT%i4L{01IJm&`l4cf}vGGQYUW{Jym{HI*7Yp4!NN!#Mk@YaVaWv+xttkws6= zcZ%ooVNX2#{L(JZ?1TgD;WDj$SZJWTSQZv{%o=DjlI_**wKd|NyEZUT-JWVLv+8eP zVEVLmU88++QNSw~hG2~^;qDDtZVTxY3W28P;5Pc-=w*NdXI=&Mi8`jvHAd#(^Y+nS zbG4*cJ|vho3{e`7qFA6O8C@9BH^&7(+P+;(hJJRmk};GsZ=IDED0ni51G$n_iA|X} zSDiOrx&auuJIonh?T00DzWiYgjq^q<)W1hO=9cC({Z3xdmln5?B^IBuwU~Pj(4<#< z+M)?bynJ-!w_DGyT)q4D%JpBbTSQlH=@`9;zYk6(cY=_PExN$X>#Vow>TEYCXq>qC zwxXCks-?McAYkouF7)k^)d}jZS{|qL>-5(CYA;}!9b8Ba9fDYSx<7`wSp5@G);qiLUu z8YvBc=nWuq%x#^)K(FZr3(eK9p5ay(Y8-$foWo%rkuc$_(Bisa*+{&BX&cFWDltR~>+D-QjhbyQ9M` z`@oU9{UFFCYLEiFY&pA8#@{MkxbFn8r(yY^`=?kOIvZ*8p3>kT2c(Y%+y_;xTP zn5YnU1pKPRgs6UAQ2*jp?_izbF)w{8R+>JSANA^;CfS3nedSnF=(w~<`Fhqjtg1xe zCQ6wEA?7NObd`TAjfipW#Tm3VDC&5r&8XdfA6Xqb6hU@&nBDy)7m)=KS-{M}QY7FJ zf^*g#i&d@*1E#ZOImf@RuAgKa<|iIo2YBvX#Z@cd=7cUj{0Ce87l(b7BYp(1Rgu$3 zkH&-z>zWyb(-;&pjB&y5glFQIm-A1f1H4`$on=vR@HeQz{d6vFP@-hQxX-wv47k(l z2(b}XdUKvkoPq+X@-|UmL)|K(^JuwFKo6>M4R-|R7ynQlI6Wa(Um$g`2S4xR$#^;k zQp^Zdb4n62$hMT{2Nd0>9(Og)jnkYgmDU}MpZclD zXSvNv^!Gj!-Sp^)pNA_4<#&trE8^=^?}4|(i%uKs@?4+;@+k8BUB+T#gL|D5UwYMH zg@YJ8YP*BVU9W3T;UbRglb4mx);%!7LFFaws zV@}Gw&&ia!1=7C~FnIg_0o^;| zW_{WZj=1?iTDAdzQtO`$r*Oeo6kbIBg5#c~=Y@UO@UlT@nkS!a24|`IptijVYWYQa z=9>jGF=;!kr;OXeNqxEp>1D`jccqXPdX!$OSIeyp(}mS8u-4 zqpVNcNeKt(#+QzxEvoX~UvlA&>*T^p2~9#VxZ=Xo^7-(zbZYr{wJ>TZZpwxvtaD=c z@-%5i0NPXxsC^Sba6!mv#XQPI-l#MS+1}R79yo~G^gxiC-V1-2jbHA_F=$>;d>o1m z-0`a;TvoHO7HBirOv&Jq4HWFqI1`k%QBg)Uw#>wl#Q)b_yz=h+*%eDJwe{w^ z;DC8wNao;LtW-a<=Dm35%48)K)n%Ls98~Q!GH-?bJm|w#$`!$hzzg zp{|NlF|mv1L=h9pG`Qu)KgnIncCUbgHL?MWI%?<#cB$>BzJrai0eHbkGp`&IvBQMO z;VD}+ovAciF#N`XaAg34yDxUm4jsJAy(6jhgqI(nS80yO2Es|IHj9#!GCbg{o#iRE z79m-Qv)yS!EAB?&oIpvRX1)YJ`)E@4Mp(o-3GYGN>5rO7SSp(W<5SD#b#EBEI_#d` z#VBv8a{e3N3?7T{(*0dd=f1D@TE_95;op*fKGrOSW7}mEk||Dib2T{t14EW)W)OB6 zqM)o&-bpaM9YB~O1sZPU1;qZ7cP3p!5@hig(+n+=ut{L9$f+sr*u9>|;1?UQE>1`7 z&so$^?U)#j!9fIRJ=j%T0S6`S@XHA(Fe(n-*r<$#1^X?3=y+2{7qtAM?vSwvc$x*{IEi4@tpgqK2o2~ zINm9|h}jbNvQH<(>Dmi49cDY{rdHlS+C^S`Mm>tTbTTF{S;GUL0m%yiS;uD@Twc(_ zD(#=&T(+j!VXz#tz@~g;i0o{|bUUwYoe3GnO~cW*#Dv|>ii5yJvIYj}9Ya_-X)uXP zQ-_(vt(h&~k@T(?* zPGLqrJzqQ-U2KrS2O;L-M&?_cAsJ_NOf1yJxb)jzd%+>t z96S8f1ljY@*pfK!&g3cB$*INgNvV#=MupS1B)HnudBxIbFAqkFv(j=)f0fRKmC4!0 zH5Z<8&kJjEu<+jk%J(N@->md8WtwG)6aGuKZ6?{%SzcslGX>cHf;FL6Bl+-Rb^#7* z5xYLOD9+wF4Liom4bKuUS;P6~JA*;AWp8JEJ!oL_y{f4lY1)5T7%Xb`%Sr_ zavBi;dQiV#K%U*t&{y1LYtS+ZGZw$dMZt2nQA^QOCib|YOjzEm>RyVb2-fcIJX}Q!Y%aRbfN>Xk5)okY=$H4iWy8lt4US3 z$>6eBPFlI@?zIPnaJatNDQ1D8dC2XQlk8S^s$|SIm;mQ~g^7%d)%)tZl+L%r-fvu$IR@qH9TDchc-eD6=>`Wmww}=jqF@8o zIH^CRuH~Tq9A-*Ny^=aAp*+SSu&09YG-IaDI&w^+UV72SuYER@J)W}?*~qOJD>4|2 zD7?IqCJz{bR2#V}^n)B~=cYm`xmXK4KT`!#?V;=+4&Y0`z-m40c>dra3@n6a|vOC_zXTMz=a?O&cHNa2WS=pat@fN9Qj~=YY=(b*c43A zB$gw^O`c=6mQ6CQ+`qYf3Z@~iU-R=QZv^{P&blQL*=vY0RjRasP&3c(hi-W#n}jSa zd&CtCE+Ef8CTHv5QO5<_`zrKPSyJukcjrZMf6*#OB)^4~^3<%oNFT2vJDo+U7nmy* z&X+5W@%b;#Bp>dRy?RjUB&Ha|9usF=<+e12KVFO%a!sst8_23|)ksbb_f=dZqO#-9 z2}dic$J04Tn(q~PE5z`Ej(%2ahgH_@5=$j;sil`K6qRor!NStCYLaS6CQ2>pjOsph zCM)Wedp#NXp(xOoGURIn!gbisl0Idz-plB^w6<$wx)n*QW^Zn!on?cTtZ6ic27s}_ zrHr;qJ3m9Brzxg%>*QyW6hl%yjCCqO@jC zb59ekz;Yp%+~i6?-u`i79WI?ZEo+XaC);Q4+3U2ob&8schj2lB8yADDfDKcT*Leh? z%%DzRmpO4decU)kWlpL1VSn-}fi;~%<=L56Ut_or2KU)k<8gLu1`N*7 zik-PNTc169ru6M2lNBKh?V%`!S=4u)&I{`;^?5i`4s4LhD;An9C)F%-|F68o@r$=M z`iOOeFLs*}ieHot``&yQFh@L+l^@OEvE_PvSt}5wDIk+hrm|1eqZXK$R>hgCZS5e!|M_U zr;Pu!>pv(wUv)_fUZb@TpL8m^oozD3CwzMk;x=}$uM4VmRdG$!e%b|i02ZSAA=HvV z1I`D^<*$sxFpL<&KnulYofk>d0+$&a+5Hdr@Q9YUEWvA&( z-W-$Ri|@U86hH#vFiummR6R@ykEa3uctNXQX@)vD-Gl2k812p>>{d9KnjOy#re1~2 zHu?_f|3Yar0(hM=q-l^rvLgZFJoT9Ne1%n9AhtH|qOr-5P^23|&F{%*<2oWMHzQbn zi0MdoT9w_awp#pcC_po9?bvkuTrW|e2$!&76ry~%Iq=GoH-gcA*33W31_Rl;F z5f=ujr)nt#sS0PFDzArS*k^&2n3DDxCp=cP)3t+b7j>q730B7iH0zX0wlE@KGLCPR zb7g4V0N!{+W4_t;rE6tB#B6^GGsWU-BM9I5nhjnFMvqL5eKEmXVN(ev6{?C78|LJ* zneKbk%123aNLp)o;=Ojv34b$_9eNI(#uu4r#a)(7yF>XQ+ux;gH?g?rqe-YuPm-Kc zcar?pU`z$_^K}}a1~7?eQb&zCeJz!55O_J70DPaZ-?=7%?6CX(!-b!Z2nP(Vim zqpiCkT@k(o>i=dQCpnm;i z3F7x`0%WK@N!Kv^alzbOtlC_Atd+;$eE-&LtDmN^Wr=A@pQ{V1zzzZCJ4*~xU}_Yq za%kf5fQtF%oVU6d<}+ZB_E1+tO|)iOf~GAfZMMKJ3C!1)9#)z-Orn!#kepjrCU35L zdy>}v_>E>CgMm@1&v`FI{X_5~N8fyin$WP5(WWYd6b|yXIpXDCI`6?1U#~i~Y-mTa zmN<_7u5l`ROP*mTvP-2^4d^iGf*uZkuO9J6-6Y_$>ND$P=jtn`*YUt2Qa=bE5%8&S zjutQd>d&a(KB$wTQkkZAfpT@02V7lcGi%T2>030fqJ`*tJuo}-DK#cXzx@jg`s|DL zFDN9cm7qy^{M2i%KE8{Jf-;R@>ruG^laS56OL-l$Yt2{@?sl{NjXXJ3SfMQSW*sb1`gV1ijmyrlG2 zJ-+h)8G=B~qPt$Obj1U&P&rSZ~Dp8Wa;Z*g?DVqqe}S2< zyqmj6VBtizPTkZmPf`<}O&W`dzrZtA4M)%-U^|s2LaFv_*B`x-xW4JLwY-ntp8JY+ z$;?uI0D|R`cL%okmg|G{V_y1m(k}dJs2s0^?U4R7JU#NTvxsx^owkOfg|A~SB10eYmOS}`geiuCcHi}O% zJ(|Lj@n1>!uNWr*MNf|VwQ}2CaP4&k!N2_KU-Z(W&k=;r(g}?9+|to{JS_jeY-9Wn z%F+Bmxto95-@khuzx(ucc&W%*m(~BWg)8r**$7l9ChfBP-^lQS<*k681MlGRza!G0 zHu3k0|09dM`+uRMch)o6|LO(sZ%_9BY4yKX{0D3QO~?P4iT{=we--9`4yOLiJ%4l0 zf8&q8_2U1`6#p-J@o&xbx90lWI{k0X$Um9lZ+-pWzA}bt{b<06CIA zg-`c z*1ezj8rzK${@#VJ z6Fdw4wqTKwd*dv4kuk+_fo+%bp=^}!V(32<&8vW`H<$rA9=%2*DCYT%_+yv)44FXv zno0F6G11~&U&Wf0kZ)ud+rv&A{A4D1VW_%k@L^&vLiL74(4s_L@aa*qs|cdlT@F#8 z1h*PGf6t~you$R1^mAs}9T-O?+iT;b_Vf41Q|`Tz#p5oVk-=3gKN_2X&Ir1+39%<^ zf8`kb{bZ5ws01bL<4@lqIuaZNFCzaT1By5Zhnw8F#Ner%Qg7c8h_9Od8Zi6CxM9-~ zf@<}PC;5%W;EU6w6K!~@S*vfa>uTdz!@{|Q+phyOZ2TM3WGcmLSb z|K$>vm=a>JDMMM%{|$rqkb0$a;yLOd`{}>tv;65U|Htd!EB;?d{YSFryT9r9XLs%Y z%iJU7>rP59DkSPWAz}h?6Qmb+`^4Mz;vZt_-~Y`wU+xo7*bmfgyZLGWSf(iKWXuHF2?p-I7 zYm3~N!Q83~#Jl0fBAL=txDC2#Y#h<|pB{|L!^W-=N&M!wUl?U~a^Of6GHfbG71N}e z!u3n+P_^E7ihLG#>!DRO5^7p4k)L5q3V*N5IQ-w`0@+Q1$T)3I!AzFhk4WHy{3!2p zXb4yUa)ld0|QdvSwi=6uQ*#kssP#1A#xlvJ(z4XNqqU~ACmHXmU3 z``ZFQL2s-&L33t5AV^leIv~zY*mw9romJ011$WE$&KPwn?jP0W(AiH9C8QhA(ne(G zOg){D(>@*~#lyy~JLl)`j$QnBLnAZ0q(lX|kuIVO0pD|)5akoL&{i>F{DQ#rHZHFt z*iKQ?Jk|G-BiFmuajv9h#=KP&st|{{k67Q zO!;|SP4p{`VGn0IIwTH8Q^6WJl8T)ZXlNiTmDgl=m=?ip`~7(pZHX+Yz__TdGGHZe z>v%&F3ADQVk>yn`X6++77=Yy4!$LQXax*1uJ?Hq0{sL0J?x1*3b z^rK{n=x`@2Z=9#Z3QxMPDaqCx-{6eQgrI|DBrKlX-&}7wsO2_QN*LMrpv40Fp}JYf zO&Q^_<}o$1mU)wuF5R{9!%-0pD4tuJd59IQ+MNQ+k?Q+&agcO-CcZiu)0fl_bMGKz zbh*v9aA5nIj5&-fL!53}%2MvJSwSz+tsaxvo0*J9gAQ zH$=#dYUt7TTghuD65I8{fFv-1_MAx^83RX5eBWVj9Ff0r(B)fTOJyRoB`2flu_}y%omI zCios29auLps4unkXL1$TIZ}}m zqw!#L3g$lD*7XULN;mHH)1CxYoHQPIRH&P5FWkl0(H|Y$3&^e+pWDNnGu=GmSfI7K zc_2NZC3)GKHe5Bomq)ik`{iz9tm}j`pgR7HRTRpk~#C4r%2+d>=fDKXRT7?LMt2+Ip*k!;j05Xqaf{VwW2WDrDmtIy4A48~t9llv96Hk{!qTi2YRAjGE;AyB$>_7@{48LS^cY~W zXvOgh@y1DggT+C&?W`@(A$o)E)h1Yx2A5mE!*FXEj`5dVwq^=2?_-d=A#0n zG7u(z_v!jz!18n#qO5pjeK)%6s?UdOnO zA_JTUas&1|B#K`3txl~^axXFqcJ($IXoMdToQ$cdK*!?kb(2OHqp0S3;|4cZ$!S+Q zF$afLR2+0htU9s{v}{>}@U<(wCMB|ReY3S1A!%B*aYHPAYJX(|hg5#xf@;h`xeTYn z0Xp<#Me=2zBYF|a=&$OB=XwgZ6d%8(@q!;xR^ z3Xf^r2U1Mc+vbDMM>0i)Tb@cRG&qZk?}sF<KpHr=*Y@cn2Rir4osojDnTdZ`(@Ck};%Tzc& zw~YMZ^7#?^2_7OWRR*QiS_+4wms}>AvNfV|Ct2JLMp;y%h9~f3uW!cv&T{P z$otir;!?ibVrvWwGY8nQPM12Xp#z}vFg7>)^VEH1=Id~Ys-z9a(2n%hUg^CP5uFZ= zSep^=hBl@rMEocx;>FY6jjGsGFUVRTSYdC!}_WxtYee zjiU$Jpgm6doveXIUg%n3J5dDmBs$pMHM)G{4nsh8U`SOBa2XvUbuLQT>S1Fk4rQs_ zg??){DAoi~8)c=t&VyqEr<^owb;asy+d9sU0>B$H>_f{d8$*~JzC8Zca9o!M`%{sta~(I;hD#w?txHnm-p7hOnn1(9X7?Sg>{B5oTBihF6|8c zpZTz_vl?CJ#j~Wt4tC~kq1c1%!XreWEDbwcCLBbH%8xi_-&=q!?s0~pEpy1dB*wu2 z71#=^yTl}7qJ0R1G#NTRiGB+A~GIt9lpBcmRH`cG5lp z(bq#U*4d4CFTiBBUMIG&nO5&7Z&`JqCQ?sU5qJ#ysmtgy>Q47a-a(N+JGr-$o@IoW z&Ne2ssHKhAjW3t3Jod8bkZp|P^j&t@i-UOT1-*3%nprVu5#XnAR=mTH)=DWyeC~fU zP?Xj@vY#S`?G~ZGjcHoa^qyRk40~;VN^~-IE#AWi-oOx-vlTQkVR<=avL~TMSv5rR zC`1bA>xypc@tG}=H)cTOm9~-NsQ8cv>4F8YEu+1hr^caY#2k`iYN5ZE*F9s9(n|Wi zKVx4|5HloQt&wqGLt28i@rm)+i4VrqpPNBvV&YrzZoCtsgp+dF#2so>fq)C^1)o?i zka#Q}jd|(z4QJTr+@~IR3Uyd~fm@6$Z4RL9b$Is3?f7=GntFaV50_90ixOTTo43J| z(0NWWy33QsET~j#Z7hNmv+b_FM)jZyc|e$sYX}JA#vHOd$K(ey#8U|Lh|co;g0QMP z#3~H4)(s~ti_X->T*chx)>Aq8ZC#E&(Ii`%`Oe6a zPS$-F^P4r3Bsv+Mmf60>k0T@eWlx87W*8%yzeS6$8QoJlsw6@SlBBu;TDK?dncS*m zIqsFYhEW9s-TM8_2OoM!Rym;!kI&EWp$;3!`F^`=*GgSQ(?n758S*-)@G_joPZZ~k#7@vKjpX8iRV%Fdi7{YG3aRFyVP!IxdUu#^2=TT zr%_qNeZ91)?l^LaVyF*k-9NJnJfEn`S()z$E^X!M9freG)%Z5b2TEMg@ zQUd$6saE)MUx~xBrxG_*>pJ8$A_Mo4?SpaznVrCj1bkLA>`~w7W5)v`FzD8wi9^{m zX_DQb&>dQn#&wf=9&GilsmQv5`Ce_j?Ah-=Q<0bg1lq(U1uBaUUaMSpqPfuStH36c z%P9vVryBBCi8*p@dgAlnxLMwy8x@=)Ya1YPo@fav8M?Az- zRX3fddaEZN5nQfCEF_u?St%xg+42Hx&-!%EOYP}?(DsIIX6XRU3#jqD6sal@Gs6hS ze{sUU|IYr(&T^ATJQV3$<|`O_e%b*mF6c{+>MSkJcBpDQv%-yW6u=f-WQej#ubDAy z)NUZ!1wf&)?VWcWY&HdbUqW1#KQ6jWfZJ$o+)ds+8HwqxiopVt-D6Cb8x~jrM!EE!u*$4ExvE$gPm(e0A~pc6tTVZKegJV6!x$x zOY5>UYcb>pzia=9dpTn#U+r_Cd(;vigK=vTu ztPffF=k!CSH_1{;QNzNO_7RB^2}jS?OdP8nGCnOI6iB`TDeBs|NzVk{DjEHrj}0s z;nN>+f!PLWxdz;F7o!1cm33%isJdAoHX!TZ_wyaYv!DaNkWta&=t7)05 zi8F5UIAloHi&jeOtkmoR@pE->${`C5aub;t8^o6Qbl7+LiVA|*@DDVOzsD4EFlk^O z$B_MfNUgtvfFaCnxk%l$1K2$Jgtl1A3C%NMI^>xY51-mNequuKYmFSCeks<mSOy+4SRPw!`c9FI_XbUz^oo zw%+oXRiBuAv#8t8oPcostD8^B{!WNQXTH@(bm4Tty(QtT?Y&|R*$sbO5jTHB=x0~4 zj-V~bQGDZY-iO=wIxhUB_6n2cON00Kl=y7S6mp7b*xMjl(^|UYC9rYEB+tPoy4iq5 z>xhm^x7z64>=kuU$$nmKSR7@?&qTF(rov5^SNU(`0wlIRe{@T$kWPPkvi=T?voxEd zAXcS#HlBeuMx2a+LJ}zy-M<{3ZB9rm?iJk|CIpOB4b-QGL~k1uc-YcemXC=0nD(;* zN-GbPnlJjvWo##o3rB7)jP0$`SF?3ZP1G}F+M@`L$8NXJ@K24~+tz1IHHysC(BpfX zndDNW=K+_21h7x?-K!Uwim7Qf?OiYW?nk)YR+_fI->Lm#YVWk|R7BO3LE3|QRdVcP z^uGLX@JC;|Qv4!W{(F%MO|qbP`Nc6|3w< zc&{fg)V5A;f%9Q*;B=~ok4;P8GVoQOkY2hxFv!k%kiSS;$E0yvC@rKz`OZhw1;75N zW1Z$`hO~1-^L9z;vk+$<%stQ1)q-Jl z7!CK+_kAx(Kfr!xF>aZm)qm5nLDO~4g%3m`i88I@0sd^#d%fPcYJ+IQMmKZh*-ATN zTevr!7-5hp6^eWAiMo08H1|xQ^MfZMHD?ur@30kf(Rf%sb-uP{pH((hpn9|<7i=X< zXa>Ii$S5J_l|A%@9#`_D5)+{f9#0ZxW873OIOM0bPOt^h0VB6fHK7*G$XVXgtrp7@ z^^`5c^A#seic4p(U)mFw1__KQ&57oUe07Pje%p=@YTkIuWo{c%I3Fs1%CqUYeONU| z(-FdI-7iNadDbe~$O0WA<+2VD(NzgHdBOK*>WidR zA{hg-QJo#}hdin2`FP`Oh4B0)AvRtTBF{Dg%-8dx3rLO%gG|(^?3o^(ta$;^7V;5{ z80oJ|=z%e7!M6gGm3 z@wwu}Q$JRDgFu4?XQZd+_h(f&KvrG+Oqpq$x4UP%aHThk!3v^=FV3w|My-+!w#Z0v0ZcnnQx-PbOQ!V}(|7;%*>x7*IMiyEuV8ww+6FXa-lHx{dZA&P5gdoicRoFmU(%p%l9N^AG2O% zkDa`Fpl==vc`V)PgKA>!@1+?VP3bxT%U9qruEwx$p6#giDf5-+d6OdbKCcL=9p73Ots3>mUGM+=4 z8ch%V{cN8pYNTDml`eB(KIOsd-JWdG)zf%NGe7y$1~I}Qu1_f2Y~_O_>>a^t0ql}G zp_d2Oaho3{I3w{Ns>!}KXV13U(#)UL&bZ-Bfry&*^kkaN)yT-!9t#ObWb%og;^;gL0dVy%+G>kp>w8y383h9{bO`DXfCUkFI!84o7l8= z4?pMTU9nM-8GLzgKBhUpsRs&nT9rG0sXDePMxy}=4BblQt;cp#F;NrObKa=SPVi^L zk^`R)d8w=ty=S{6Ihg`P`ZUGp^Bw0>qL`^*4>%I`)(tW9|?T~Dul~61!=4}2x>Sj?Py+}U3qy5@4X;llp`1nn@ z$HLeX3%UA#jIDR>jM`C@}(iqq^1Mcxq@oj_7C}xr>f}1 z6OBpHLTAnc!1w~|30Z0nP@0<@16;HCKBbu)M) zk>mvMJC5l1UUB;lqCzR|#ZalWWTrg(mTu_UHpH~)Zim@iHTDWV=eCD!J^Z;fO;g-^?}fi z7kvOBBsj5ax>&OB&B(oZ#h`U?VjyoM4nXajAD*i=tC$x=z-NOX!1Q8fv*5+UEY7X8 z&YgA1gr0EqL}ZGzZ*B7e0sf*aQ)+JVExPv%noh&8IA3GULC-F@$XPI&xtUC=p3|hj z{3SZ3)pEwA*z1>G*9_fhkDbgYPB^XoYFz_5e6-~zb89lA!>DY?f}n7=Vr%e zhyfphJSEQea$gcHqCh+X@#oykcE?xYvYxrPPqS@4#m!qp)h9gz7C=i;chkPDdWi>J zbB6m@k~q@0pkpL3Z0_0ILsw|wPScJ91OP0pYQ^%_*#(c`w_Te_xTdv0Qen^mAtC9#F$S#e^5X@f*V%{FIH2c0WNUa-^VRYn$A&0e#6B zpW9>W^2*$-)i+hcSAm>`S<;>IB;lOI!4$MeWB!hY#)_Mg%d=+?P^e#Eo&r`lO7}vl zKjwX&z2;9S9fLRc+p7;-trpDZHv8-g^`-q;T+-xlmMjd%9N*mkVeBoV;^?Av-3Y-FVgjJnJn`GmaTP+$Z0b;YPi(InN zS!m-(yyOcTT5+-tN0Ketz|m8bbGXqgeRR&HLHb$MQ@_T#%;ix^2uK)+2t6d!>xILL zm#1>`m=A9ct>{Fw3$o}W8Wk43_F8mI=~0;K?K@Z=DaWQJn9OpXF*@hBMfzsrr+Xuj z>m%?a)I)79S5oc-$%aX8QCZ8Xf5=Zlj+sSl+cOC4VpkWq2^N%G5;1(fEOym~9WE;S z#v{{4;lcI&>}ovKJ>`r)E1t*$UX3A89eov)7?-}{5$2i{X?l6_sXmcMx8{YobK{uf zRFr{^H`flx^cnpF8Ot)KG+O$k+IO3q9-hrrsDiEcU;r0bnJil)D5OPT(n1XlVF=Quf<9l9n*Gs=p_kvvNYS~}tQ4ih-Og9htmw{bVj{|cBwYCC>kXL%Oz zOi;}tu(s%p=l(R(aY8cjL4iq~9>;BJ0)X$$Jm->OM)evY_sPFcQip6eTM zG+c=4DBQCs0S9nY+6iU^jCmg&`?%d-fTIyBN*yGc!M|MYgmb^iB`~PDWp6EacV+n! zOFpZy9tv%{szV%<;bHRA8&dBu1sR&qy%An^Eb$1}`c~i4!)m>5^?**O*E4G}YEJ1( z(Qxf-)b4+}nc$P3FivrB32;9a1UmOynXS~Iod?<;e3t^*Ef`w>mF@Ykj5KVQ>Q}=x zms_5`itc`ANss1_37(@AbpiFu{i=fd-_$3bhGHtrxHp>u=TIG``RY<1K7|0>N zIJ_&`%Tp}G=_PraS>=WExry8lrZ#q97a(5MwB^0-eH`<-Nn#<6@Rz%0<|ol#>HKye z`RI~s{Y&!D@|{lbIyb9c<)6=nqqEF7Ad5)6*9A$0F(jro3Ry%Ugi*=Y)VTZ*{etfW zAaMk}YGc5<4>;@KXxF!SLHz(mv~Wor={4n?hH6oYvw0}X$H|jS_nRI|Br3vq&CK9r zBqV=Uc&G!ME{$i4@7)+Qi&mW`a#+7)Wt>)%xiDkwZt2-_G_RnL&fK6kNB|t25Y207 z+q&v2bQ=E+zsO)zqD=KzU6c%3tk>--J$fR|>?#j-)n zu}3;plI8Cj2zdE~iX>44FBd`)pk&(q1S1HIp1FH!Bbg^qJI+pSy(;VE7&*QqpEpXj z31QJ5k~VO|R!ODhfppzKf8>^Z62!qZc^q7Jtz-p>HL)#_i*5!t#C^nOH(G|z zOY2zk6+^x5>_*KzjG144tW;6@;!?=8AM>-(dUSf_U`fuvm6#K1WnF zzLp67F{HhtD|!UoOPP-QHPPtcWn4&~(nI?UGc;Bzl&ue~V`v9D+0lP+yauUS; z;beaM1wQTcsj-M6v&=6$=u+Tu@e`aTS2ZB)2_>_FCp*xG-vcX*2l=mW`s@@Ap;HTbCHK zUu>jph|b>}&aZlLXI-xmX_gBEtrdVfDY0&&6iw7m>vefg^-Vb&ZkdNO zO=%V2-(l;X8740Bg20pbh>H(S{(O^<5Xqe=9mY5=uh7%gra|M2&YQf0eA4+copGj< z-2V`bR52nJ>kUk2}Yi{>u62*xj>_N~_z7vP+}>6Cw)6a@*0^_E^;>w+&+L0W<0N4tLXgYNx`u>`;SKpF zQts|iTS>yjLls|@X9^-yQ4M_vhID2LFAz}b2!%B4cA!szbd^} zhs!G|l)K+2W?0sq?gm{qx?EmI_mRavi440%kL=8rYMAd^c(h`?sW^C1uabAkGe=~? z_)>MFqT;lNdoQ@sth>t#yw*}BWHxRhN#dr{E#Br+^LJ zcP)T2vWSPhljFm>rwY7)jf-*+$@BhljsE2KTMpliD*{$56-{A@Bd+-k_0(u)kh(v#upg3Z?RxBtaEnwRPeNYP zQmZ9SS`!J6Lh&7ye($_^Y>*FB5xF*%Kl1%^Fcv95NyC&?5-lRLygb$8b9W*DmopT- zAw^#<7rzIN0I}1r6mQ^>3!|H|Q@S)Fw;zeKN?W48c*Y4g6C#105X0&ea-|nM-v@G;5+2I;?Q>0^fXBFizybOStbvX{tnZ)~ zrqV9p^$(<fd@FIdx&Y0tR*qBx~rXP`I$IH*+o{-?Y=g~I+4netLP7(cz!IqP8~hf$Ws z!Ki428Wb`(Avjl|!|jrHSuIrX^Wo@0e9bYxt*p`cU7XB#6A^>#(}twQb={(Kz-+BKa#5%582`OIdiAQm;a9jTz_68N$Bh`omF}ZV`X4dX zQxo!$f+Al-r2N}}0}WvK_-KRu{ZGrwE;~N4xMap0+vQC6YNNtcjS*^sCOCk^T*TBr z$w=(MN|JkJ0l&$pu@ck?p00+cO9WG{C1@62r*zwrfK|g&cFx<@PLy^!wYz~tGk+z$@F+Iu>bhfO9h3Vt(EWBBV?8CZNqDa^_bh!eGAsx;Z>JRn~G8hYU6t+ zOGrdmWmQ9~a=z}j3ONABV?`9#T6u?p8obo=g>c3L%n$Ah&PRVtFh9<-+i+Z zI{YZg0#zdFvJiF{oy=A0MMKfhai3Cp@ zD48lAHnUD%eQ^6=TE}JMNuDvdyLyf5n@_!(NUC)5*(IRTXHqEU9hRt)rU)}=oIl6^ z%4K9L?DjW3EjYx_y1@QA?;*C++RA_l^ck;jdf)4hOUDD~n1e1}viL&q#P{(cNP}Zc ztySWLDPpvNAbI=_!pv;Yd2Strcl{{QXcl6S&f{K>_I9?($g|+r-a1c&OP7j3_y3w| za)|Q!R*ixXvFgr4W2&!z$FVDjBbBS5KcOu6OK5j&{e zhZk*9{b`%5KuoiUz#&xJzeT7K{Qt55@_dSi`<1hG*YTx4DluvJTvj5?+ui= z@tbwuBezJT2PlJ2XRva1Tm{N{QqH~CDhGk<3aaoD-6_sOU%zG_UUAX@1lj}J@7;* zb@{>(!!vvb*~E(6XLm+prqJ~tIxSgS@R$OvAiI>Zdr$ss@Ish4^f5_qdL)U}BwGfB&)ccn zy1%Xn%|&+^7Di!o#c;M*mr(I3bex_3CEJAS<5#XfOWw!{PCtnm%-JErmCa%a0c2>Tw^Z ziuLaiT^P5gOi@tBnHQnobPp1k!5?PPBp#D>QcX>?_V}M;II_JdpdOb&#*bI6e@4>8 z&JvN}5UdOliysat^mSo|@MvHq)fkM==ZB2s{9=S3Xt{ ziWX?etVUy^_)}9mzRsE$)4q&+PTW;?O3}X?K+HnN^5dST2zE;=*OYbjHo6u%M|pvI zT7=0GA{)<4H^Q>=c25vlXZE?jSDk<{ZmU5H|tCe9esKIef! zB5*U;x>0Z%?hd=Gz^SGTc+_ys5)tdm)I%k8g}k+DzcnB9x3nA|6B`jT&=6+uCZAd% z13VAAaZoPFF0ZsW0JALCPMKUeLBB)zud^(+%P^7LEBWo_SK;L__A^wnEsy9f2tmS; zq#+a@*heH&O}JKY6GFAcT2EEFz$qHhr->9TQ6*!P@%@nLI{*7EriL$5H2X?|H;d*8 z4+yjV=2b15BLt%+M#sz6>-lo}F?Ip&2*ZhkALGWMAcqi)nEioyXIZbz{TL#-Mu$ae z-R$GRIPC>5MfB+92O{Iov)sff*DN;(QI3qj8k8@#lZeJF8NxzSPc9a ze)=I5b9B__stM2^EgcD(-mSYia>An^{tt6bX@j7nr0K_W38$*IhpjCaxlbux@k6FZ z@OmTUR_l;2--Nf|QbsZ-*mFBxK=Ys@phIjfI<0+-O}~9=Ze}HP+h-_r_xhL;3KrsZ zJaPq2_jrI-cSJJ;*pJ%WS)QwqB7ZTWNKJ~Tf6?{l14MXjPReA)V&|8waRd=?_qm)cFV^L?cwpnagL)?ffQ&;<{OY7d=-%57(N_Xr1|q zGd690n0ii&LvBwa3o0hZ;>Ql_TF?9$L>ycz$(+|wKe{n|?s!b9JJ-i6#_GZA0zXfE z239yMzb#R9mRs&j=I^8Q+ z_2!=+Th#dM%O+8X=PSSiNR-ih?+H0EiBf!)yW@Q2meobz3){=R_t#HX?$8Zt8Hb@| zzta-%kumE6HDjn~?8B!k&PXDpqWqF1yfBMZ0^;x& zEe@WD_oz^oW4OWfEuVFG#=PeqF|@Pz9A1)VK!4rS8x*o#!fG7*ynD(}L+^CnnKD$x zTw|6M*3#Nb3rU_6)hh;S3yJz|G&;`w-m99fJ6itnf)rEj%KfONaUpI)03BNDv^ku> zXvDdfzZ$x70;@!RxyR2bR6!Af?z`blyDZQ3`V`y(zMmJ#Nf{0JU%4V{W`-r6&j;#~ z?i*FO>MP9-b`bKV4Zm(X*l|tgQC9+g%)edMAxgP@!DW5Fl72Rf;M++O`NRv&Jk-h( zr+KdXk3Lbim+LqEe>njZM3I$?i1ciKDA%u62Or}ci!sxu3Zg``VBVCVfk~(t zZ0hH7sElT+s1vONW0Wj|yLF@O+z2PfC3wjS-<2MxeK5>+O5TZe*cJ0hpY!lV-_+#c z2!~Pg80fcm&Eiq5YE)GtTJtg{QUnj~X2*lEWO`iWH^cc7r|w;*L!v6A6zQ4)gH%zr zc2jaWr;ba*k!`NA_pQxEG7CBSIq&W5uxN}Q+ZYf>2>N!;a7GznqTVSiZqcyP<&h|S zc_7O)1SkNJc$s_I>8rn*F{s8{;EZ}w``<|zSou-olxGqHZq`}YCH+Zv$w2DYc7~IU zszx5^qfUPO#(wWq1#8J4O}Z~nqX#=c*Di2T^eL@b#esCBd7r&ypgLudwx;wC9kvov zWP?q&*Q{I1@oo`)!a<>hMulc49D=@sl$DEbQsNb1jXTp3(pLXie^4VFAwLITu z^ZQFcNV~cl&lFdvIctjCieFM~mv|amUm^b-3VoTl7(cOnr5j?=|4n6zYZTCSr8b(d zwhxY+FXD=V>n*NVVtvPQ8_C`qmLdvsddameQ#%*eTP+D#J0A3V`c z1M-ks%m`uRb^{;1x=R}1#$bTBgtl|*n7TseCG2YYX@vLqmFT@Z*k!g@CC$FGK$H$B zXXAIzj)&?wQeVEJW!Az*e%U;=DC!AzJAn4QBxyKyETVv za*k=mMQ2@O1W*zrg1o#EU&&r&y(@}$qwrX$glDp^^@Ys89iGc}hxMdYm7>&VBwJvn zr|(4KzNy_p?xe6JJKNQja-ygO`*teM>r0;I-#_st2~9L7+YeNhNcJ-Ai1}P3CxN!3=_kk>e>$Wppk1Kl!{Z1$Mxk!2Z%1*4SpRizF{mlYr5Xl5j22)e+kHICdpK-HO=eI}Ng6d1P!R^I# zu_hOtvmLx9%Yr;uzE-6+A8O=Bn3WNHEPjz)Kbyu9UEqT*^jcQ9$^>?E1W_#2yM_CG z$3Y{S*1c^}3mNB+jjg3;qm; z;~RC&#;$5&!R|Ab+goftsOAXljLHr;Gw=};ozc}xh8IJzz!iinc{Z<$YoKkI`DgVN zFlT1povRMAQmbYQ9wPYeZOu}lc~OQGe`7$6VLLxkAq=%(Rcaj7Ru?#?>lq$h$tge( z3?a@mM`AmKan@;O?~r^ce5tq&Xa0TfOzwck=c~;yj))~4Gp~8jQc|o$@QeFAeP@d0 z@<6*&MN%d%#ou_~doGT<&caL`k$G#OzE+XHVF(d??sq}%Zj~BairLnvo~dXFEz3RT zv2~MAN7W*Gz$Cp5p<{ZMbMO-^&UK^h%04A5U(oM0R$ZXGR)!3pEkrFxz`^jhQ}Kbo ze`5F{alpZ|uvw2TEuZ+)ubOI^(p>JPL$4J;GR~#fQhDn5zZIy*>$y?1nrsblsBtMc zG90i9d2s1S47lZ@1?*Q-1q$D<3YXEACu4Nd6$2=0TI->}v<{G4>`{->LRqt99OuRz zd8n!OU=nWI{xZwX6RP-BM|kG(^-v4sXujGJ^L+N#(2io@2OE`bT|HJ}{<^$N{C0KW zGL~h!kM8G-zqIQnbA)w276o!8WWlmIKiw0+m%0>d^HD-j@9`F5o)eT|%5B&-MOf#evp{ z0StN!+uJxNJ@u5bk0Y0npnVkS9So?wS={s-ukuJXdSK_;f~KH zw>yHDxyHA5`g!$0mks}BYJPl%gAAj2NIbR!2KG#{wXq{i znH>SY<0y~CeEDNe172-`t&R29VFAHpFaMdd@i&zkpXKjE@TZ=H`pa0C>;$Tp#@n_S zmK{Dn3M6`N9&=@JpB=@J$Epis>*w<)vRRxeoJW$k zomP{k@T9Q!zYna{!_1KJIlld|8_6l^F!AkD*dsS?cPld+aH7XWzE+8CrcQ(vXSodXRfr6gNxn~x`scp= z9TU^)*RW{lVkYyNy&7MLNhFDB(qB?#t}_Y~WsEdrU=Nw(>B-0P;p%yE-K%F<$N2Cm zHxoEvK!t*m$eW9-sE7~`xg{&5>;#9Brtn&#s{lz%H?-d-rM!pBd{ zrfjC(X~PuOUGrfw1#qjnM=&%=fLvYe{vS{O>yNn?*tuLfU#j2ONxy>M z;(vb(q`wF$`{d{4<@(=W_xJ3FFUauX5Mm2~{M0XTss8)p+b7kpecE+h9b&@&)1@zQ znZyv7RN&UH-t_M;Q2vMGT^++aK)Jq|X*SvabmCLn@>q$RQMk{1O8nhVetOw1G4Mm z{a-ZO{NH9f9`ym_tPT*F(VQ-Oe%C5d8*VJq`n*3u{(!mbOUApQULH=F>v`TaoePi= zc00(8hWb`!3c2Km=ipSB;Um{_GNI~4xYParbf*v{{5P_&j`kl?PjxD7W{RsS1`3tT zM$XoLn$KJJ^hjwoDbI-=tCue}1U&mMHQEGKnl`aaLasY=RhH^q`!NLN%Io~sMAEp; z4R?W+f^cv{^3@*eP@TDg#YUH3odB9i_8GSf+UQlEi2IRpj)Wg=(L^S{Otbwu*#r7z zv16wm#A9sS*XrJ!Lsn{Z9ZN33~4MwX_*8e(kmx^hlk>S(oC7b0Yy9+16G}tpP_*j zi0Fy7Iu#)l0auAi^>$YG?qx z+wWS{9@at=#PhC~{3BoGd=@4IomP{&bSAPGXeQU_bG%Ps;~3Mys4@zBFRFa3TP3qV zFb6~xPh(?OxqV0~>Mzz&DTk>K#%2{-jOw}?BTf18S-Kex?y3!hy(>R0xz7}y4F1Qn zxG7Eb4sayhI`Rz|%!@Dm#(pfUz=TfeW5Up&kz=O`ho*v;*yvJ&!tYoZ-#WvaE=IgHu^+NDjV?rAnyI*O;Z$*z>T9 zEp=^zj2!XoNiB2y?jlachf$37&Ma1E0L=<-@+Kvp+3a=ZQ+kDcPiv10-43U{M%J6` zQlcpZ1tzbq>o$Fxa`OB3eT@_BDH^0hB3;+QIqk3va;~T(h?j22E*|jK|S3A#l61IIu*JE$apLK zeYA|BL4x`l8iE@%^zV~hap-mi{V0sHE`TC~D*D!h0tax20-x=ykS4CINVt3LW_u74 z)&bKR7VQe*t>EqPLdr-|g>~Vqys+!8bDnrsC%*7f9F#%6q>(=|_k^8pUI zP*M)Rfd&?_*m8)l+JfxfboRv)*34L>{G`wE75gn`=Usue2Y(Y@3F-Oui$|8y5<@I+ zrU0S-RoTsUyf?#D6Y@rt1`USrt&vT|)vf!%+=6nGZ5xhR7$*6TH5PgDIcu%OW(T7; zG?swc>pA~O)SN!&7@o4%XQX0-9}^hWqazU~3ReSHTii1QG|M#ihN!7kasxu)&wOO2 zibRLf-a`^rn0nceHZ+ISseLH|7UJ?pPnmHdra_u&?k*uIs2=-YgB}yr-_aY{4IqL} z6QOK7LMnti=zA|URzByHJH-I2aXT(iORKbj3dlPcRd_hO6>_dM^4RS0f4DE;);Nw@ zMncxaF8;`>SN+`!xR2kY6>eZA%TJ}c6;G?crvvbEKRHiOmv~Yfu^o{KkHaR8BNqip zK=XCRUbWF2BYej^)l}Vj&5Eo*S1pQA@)`;App(?>Zv!)H@j;xmwh#L4UmeZ;jQcAK z5Ip=ouCZ)5kk*<1PObO;mV}?r`!u-Y+T)6LuR^beS|-%=bm9$+5K{9fa6?NgzrKkX z^Z@N5mgMNhO7nQwFM#3GOV|Xl(}~jDF3~Rs^kEZ3?Hg+d7T(D$Tj;w8m)M&oXlI)( zi8+c`6iq*G{v^&%036R($EWdH6#^B{w4#vxyP>>HVc?@%ZBITc8i zJoLuOH7FFLC{n(%6haNX0}a`dPf^;P;j{h(6v%V*A<>3&JN;71(gJ;*uxavrsQ=TJ z!&UOWq(RM(wck|GUcW{|t8L1(8NJ5&h1=;edwpr8$RSSCE16)OiR^>677x3TK7Q}4 zv1xdTR=b+AD;s#dqNoo-rTN>a7T+KOHp|*9I+eSvY-UeQCsW>te(YjyRN^wozXr0IHgc6ChkK}|^SKNLmbTgMDGTc1Wqu>z?|VHcOL|uY zS0@}P*)l37T*q*N&1L?Gxn1N|nzYCZ*x+mZ`JPDe5E?nw9KxXlgXwtCJ`fynU;hk(O{0nvO z&em8{+}p=a5hVZ%zei4%8d~@q=EwYe&(~V^?k{(mSy*dQjHlHUcJ(3?=Zuf-0(U{G zQwuMFMr`-C{-2GyIlrcwXsj5q_-Z;xwm!Rbj*}c311v@oT^s;aca%~_g{S-p_%YTq zDJJVnm#Mryx`khaI;htnr=&K=9HOM1{OYuF>W98QO9yDqe@Gq4_9pO{AHR=q8uW`t z`@9uUo4Fsz5ae3o!E;#9j9a=r;U0{876atOUX|P6D_&cUzX6ldI8TKo%^%wWkEz}m zrIv2e^fjJW=u{-z`J{%OE?%0SJ@xrY_xcp&uBa1NxZQ7xtu%J(+uAA%7A6jO?VSwA zTZ|?jT?>jWa>1{rIW>rv2kj*G;_qi3I)x=TDhfBFg?^H*BPuSsXq|yxC|bGRyZUM`B{5^^7?VC z@X2N#0M0a2o!IC=^a0lk^Uk6#QG3MS%t6H(fa1Gb z=8-Bw%Cbnk%9?qrC>RXumsPXdx0Zwh&|D^hN?+Z`=o<

  • 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ȹpKrQcS&1R&RB9+iwqABKdhky%m-2% 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

    &DGdfQ&J8;&twWOX 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 literal 0 HcmV?d00001 diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index da0618fd8c..3c03bdffd4 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -76,6 +76,8 @@ toTokenType locName = case occNameSpace $ occName locName of _ -> TNothing +-- | tyThingSemantic +-- from ghc source code https://hackage.haskell.org/package/ghc-9.6.3/docs/src/GHC.Core.TyCon.html#isDataTyCon tyThingSemantic :: TyThing -> SemanticTokenType tyThingSemantic ty = case ty of AnId vid @@ -92,9 +94,11 @@ tyThingSemantic ty = case ty of | isTypeSynonymTyCon tyCon -> TTypeSyn | isTypeFamilyTyCon tyCon -> TTypeFamily | isClassTyCon tyCon -> TClass - | isDataTyCon tyCon -> TTypeCon - | isPrimTyCon tyCon -> TTypeCon - | otherwise -> TNothing + -- | isVanillaAlgTyCon tyCon -> TTypeCon + -- | isPrimTyCon tyCon -> TTypeCon + -- fall back to TTypeCon for + -- including defined by data, newtype, and type family instance + | otherwise -> TTypeCon ACoAxiom _ -> TNothing diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 3117969c16..7fc909e573 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -4,9 +4,8 @@ {-# LANGUAGE ExplicitNamespaces #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TupleSections #-} -{-# LANGUAGE TypeOperators #-} + module Ide.Plugin.SemanticTokens.Query where import Control.Arrow (ArrowChoice ((|||)), (&&&)) diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 6554048550..27d780d41a 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -247,12 +247,11 @@ semanticTokensTests = testCase "mixed constancy test" $ do let filePath = "./test/testdata/T1.hs" content <- liftIO $ Prelude.readFile filePath - let expect = [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 18, len = 3}, name = "Set"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 6, startChar = 23, len = 6}, name = "insert"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 6, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 9, startChar = 12, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 9, startChar = 18, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 25, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 11, startChar = 7, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 11, startChar = 11, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 12, startChar = 3, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 10, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 15, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 14, startChar = 10, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 14, startChar = 14, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 5, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 15, startChar = 9, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 15, startChar = 13, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 15, len = 1}, name = "+"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 6, len = 2}, name = "Dd"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 17, startChar = 11, len = 2}, name = "Dd"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 14, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 19, startChar = 9, len = 3}, name = "One"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 19, startChar = 15, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 21, startChar = 1, len = 3}, name = "ggg"},SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 21, startChar = 7, len = 3}, name = "One"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 6, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 23, startChar = 12, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 16, len = 11}, name = "Prelude.Int"},SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 24, startChar = 6, len = 4}, name = "Bar1"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 24, startChar = 13, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 25, startChar = 6, len = 4}, name = "Bar2"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 25, startChar = 13, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 27, startChar = 1, len = 2}, name = "bb"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 27, startChar = 8, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 12, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 18, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 23, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 28, startChar = 1, len = 2}, name = "bb"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 28, startChar = 4, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 28, startChar = 9, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 28, startChar = 13, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 29, startChar = 1, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 7, len = 4}, name = "cool"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 29, startChar = 15, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 22, len = 4}, name = "cool"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 30, startChar = 1, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 4, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 9, len = 1}, name = "c"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 30, startChar = 14, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 17, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 19, len = 1}, name = "c"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 31, startChar = 12, len = 2}, name = "xx"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 31, startChar = 16, len = 2}, name = "yy"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 32, startChar = 11, len = 2}, name = "dd"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 34, startChar = 2, len = 2}, name = "zz"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 34, startChar = 6, len = 2}, name = "kk"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 35, startChar = 1, len = 2}, name = "cc"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 7, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 15, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 20, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 28, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 36, startChar = 1, len = 2}, name = "cc"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 36, startChar = 4, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 36, startChar = 7, len = 2}, name = "gg"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 36, startChar = 11, len = 2}, name = "vv"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 37, startChar = 10, len = 2}, name = "gg"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 14, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 38, startChar = 18, len = 1}, name = "$"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 38, startChar = 20, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 24, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 14, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 39, startChar = 18, len = 1}, name = "$"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 39, startChar = 20, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 24, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 41, startChar = 1, len = 2}, name = "go"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 41, startChar = 6, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 42, startChar = 1, len = 3}, name = "add"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 42, startChar = 7, len = 11}, name = "(Prelude.+)"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 47, startChar = 1, len = 4}, name = "main"},SemanticTokenOriginal {tokenType = TNothing, loc = Loc {line = 47, startChar = 9, len = 2}, name = "IO"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 48, startChar = 1, len = 4}, name = "main"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 48, startChar = 8, len = 8}, name = "putStrLn"}] + let expect = [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 18, len = 3}, name = "Set"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 6, startChar = 23, len = 6}, name = "insert"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 6, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 9, startChar = 12, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 9, startChar = 18, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 25, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 11, startChar = 7, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 11, startChar = 11, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 12, startChar = 3, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 10, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 15, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 14, startChar = 10, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 14, startChar = 14, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 5, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 15, startChar = 9, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 15, startChar = 13, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 15, len = 1}, name = "+"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 6, len = 2}, name = "Dd"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 17, startChar = 11, len = 2}, name = "Dd"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 14, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 19, startChar = 9, len = 3}, name = "One"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 19, startChar = 15, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 21, startChar = 1, len = 3}, name = "ggg"},SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 21, startChar = 7, len = 3}, name = "One"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 6, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 23, startChar = 12, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 16, len = 11}, name = "Prelude.Int"},SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 24, startChar = 6, len = 4}, name = "Bar1"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 24, startChar = 13, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 25, startChar = 6, len = 4}, name = "Bar2"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 25, startChar = 13, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 27, startChar = 1, len = 2}, name = "bb"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 27, startChar = 8, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 12, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 18, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 23, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 28, startChar = 1, len = 2}, name = "bb"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 28, startChar = 4, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 28, startChar = 9, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 28, startChar = 13, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 29, startChar = 1, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 7, len = 4}, name = "cool"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 29, startChar = 15, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 22, len = 4}, name = "cool"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 30, startChar = 1, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 4, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 9, len = 1}, name = "c"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 30, startChar = 14, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 17, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 19, len = 1}, name = "c"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 31, startChar = 12, len = 2}, name = "xx"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 31, startChar = 16, len = 2}, name = "yy"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 32, startChar = 11, len = 2}, name = "dd"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 34, startChar = 2, len = 2}, name = "zz"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 34, startChar = 6, len = 2}, name = "kk"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 35, startChar = 1, len = 2}, name = "cc"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 7, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 15, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 20, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 28, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 36, startChar = 1, len = 2}, name = "cc"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 36, startChar = 4, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 36, startChar = 7, len = 2}, name = "gg"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 36, startChar = 11, len = 2}, name = "vv"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 37, startChar = 10, len = 2}, name = "gg"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 14, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 38, startChar = 18, len = 1}, name = "$"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 38, startChar = 20, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 24, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 14, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 39, startChar = 18, len = 1}, name = "$"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 39, startChar = 20, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 24, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 41, startChar = 1, len = 2}, name = "go"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 41, startChar = 6, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 42, startChar = 1, len = 3}, name = "add"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 42, startChar = 7, len = 11}, name = "(Prelude.+)"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 47, startChar = 1, len = 4}, name = "main"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 47, startChar = 9, len = 2}, name = "IO"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 48, startChar = 1, len = 4}, name = "main"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 48, startChar = 8, len = 8}, name = "putStrLn"}] runSessionWithServerInDirAndGetSemantic "T1.hs" $ \res doc -> do case res ^? _L of Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () _notokens -> error "No tokens found" , testCase "pattern syn" $ do @@ -357,6 +356,75 @@ semanticTokensDataTypeTests = (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens return () _notokens -> error "No tokens found" + , testCase "datatype import" $ do + let filePath = "./test/testdata/TDatatypeImported.hs" + content <- liftIO $ Prelude.readFile filePath + let expect = + [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 2}, name = "go"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 7, len = 2}, name = "IO"} + ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 6, startChar = 1, len = 2}, name = "go"} + ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 6, startChar = 6, len = 5}, name = "print"}] + runSessionWithServerInDirAndGetSemantic "TDatatypeImported.hs" $ \res doc -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found" + , testCase "datatype family" $ do + let filePath = "./test/testdata/TDataFamily.hs" + content <- liftIO $ Prelude.readFile filePath + -- for data family, + -- the data family name is type family + -- its data constructor is also TDataCon + let expect = + [SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 5, startChar = 13, len = 5} , name = "XList"} + ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 19, len = 1}, name = "a"} + ,SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 8, startChar = 15, len = 5}, name = "XList"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 21, len = 4}, name = "Char"} + ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 8, startChar = 28, len = 5}, name = "XCons"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 35, len = 4}, name = "Char"} + ,SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 8, startChar = 42, len = 5}, name = "XList"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 48, len = 4}, name = "Char"} + ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 8, startChar = 56, len = 4}, name = "XNil"} + ,SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 11, startChar = 15, len = 5}, name = "XList"} + ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 11, startChar = 26, len = 9}, name = "XListUnit"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 11, startChar = 37, len = 3}, name = "Int"}] + runSessionWithServerInDirAndGetSemantic "TDataFamily.hs" $ \res doc -> do + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found" + , testCase "GADT" $ do + let filePath = "./test/testdata/TGADT.hs" + content <- liftIO $ Prelude.readFile filePath + -- for data family, + -- the data family name is type family + -- its data constructor is also TDataCon + let expect = + [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 6, len = 3}, name = "Lam"} + ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 6, startChar = 3, len = 4}, name = "Lift"} + ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 11, len = 1}, name = "a"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 36, len = 3}, name = "Lam"} + ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 40, len = 1}, name = "a"} + ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 7, startChar = 3, len = 3}, name = "Lam"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 12, len = 3}, name = "Lam"} + ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 16, len = 1}, name = "a"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 21, len = 3}, name = "Lam"} + ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 25, len = 1}, name = "b"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 36, len = 3}, name = "Lam"} + ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 41, len = 1}, name = "a"} + ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 46, len = 1}, name = "b"}] + runSessionWithServerInDirAndGetSemantic "TGADT.hs" $ \res doc -> do + case res ^? _L of + Just tokens -> do + either (error . show) + (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found" ] diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TDatafamily.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TDatafamily.hs new file mode 100644 index 0000000000..b9047a72d2 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TDatafamily.hs @@ -0,0 +1,11 @@ +{-# LANGUAGE TypeFamilies #-} +module TDatafamily where + +-- Declare a list-like data family +data family XList a + +-- Declare a list-like instance for Char +data instance XList Char = XCons !Char !(XList Char) | XNil + +-- Declare a number-like instance for () +data instance XList () = XListUnit !Int diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.hs new file mode 100644 index 0000000000..f6ac8996d9 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.hs @@ -0,0 +1,6 @@ +module TDatatypeImported where + +import System.IO + +go :: IO () +go = print 1 diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.hs new file mode 100644 index 0000000000..e0cccf8bed --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE GADTs #-} +module TGADT where + +data Lam :: * -> * where + Lift :: a -> Lam a -- ^ lifted value + Lam :: (Lam a -> Lam b) -> Lam (a -> b) -- ^ lambda abstraction diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TModuleB.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TModuleB.hs index 856160ec4a..15ae4a7c44 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TModuleB.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TModuleB.hs @@ -3,4 +3,3 @@ module TModuleB where import TModuleA go = Game 1 - From a44e15d888d4c4e9c3809422821b1f4d396a2a74 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 15 Dec 2023 03:56:20 +0800 Subject: [PATCH 26/73] Restore stack.yaml --- stack.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stack.yaml b/stack.yaml index ed7cdf8ad2..922b55f461 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,4 +1,4 @@ -resolver: nightly-2023-12-06 # ghc-9.6.2 +resolver: nightly-2023-07-10 # ghc-9.6.2 packages: - . @@ -35,7 +35,6 @@ packages: - ./plugins/hls-splice-plugin - ./plugins/hls-stan-plugin - ./plugins/hls-stylish-haskell-plugin - - ./plugins/hls-semantic-tokens-plugin ghc-options: "$everything": -haddock From e0b36f74363bc941117ff435bed9cbb009009a93 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 15 Dec 2023 08:26:37 +0800 Subject: [PATCH 27/73] fix stack build --- .../hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal | 1 - stack.yaml | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal index 47f1d2bc9a..871956c1ea 100644 --- a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal +++ b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal @@ -76,5 +76,4 @@ test-suite tests , data-default , bytestring , ghcide == 2.5.0.0 - -- , ghcide == 2.5.0.0 , hls-plugin-api == 2.5.0.0 diff --git a/stack.yaml b/stack.yaml index 922b55f461..f399c3aa2e 100644 --- a/stack.yaml +++ b/stack.yaml @@ -35,6 +35,7 @@ packages: - ./plugins/hls-splice-plugin - ./plugins/hls-stan-plugin - ./plugins/hls-stylish-haskell-plugin + - ./plugins/hls-semantic-tokens-plugin ghc-options: "$everything": -haddock From 6c3f0002114a9a29d7ad0dbce19b236068531559 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 16 Dec 2023 00:55:37 +0800 Subject: [PATCH 28/73] Refactor, move out ActualToken to Mappings and use ide logger --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 11 ++++++----- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 17 +++++++++++------ .../src/Ide/Plugin/SemanticTokens/Types.hs | 4 ---- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 804f519cef..948fc302e4 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -80,8 +80,8 @@ import Development.IDE.Types.HscEnvEq (hscEnv) import Ide.Plugin.SemanticTokens.Mappings -logWith :: (MonadIO m) => IdeState -> String -> m () -logWith st = liftIO . logPriority (ideLogger st) Info . T.pack +logWith :: (MonadIO m) => IdeState -> Priority -> String -> m () +logWith st prior = liftIO . logPriority (ideLogger st) prior. T.pack -- logWith :: (MonadIO m) => IdeState -> String -> m () -- logWith st = liftIO . print @@ -97,10 +97,11 @@ logWith st = liftIO . logPriority (ideLogger st) Info . T.pack -- local names from refMap -- name locations from hieAst -- visible names from renamedSource -computeSemanticTokens :: IdeState -> NormalizedFilePath -> Action (Maybe SemanticTokens) +computeSemanticTokens :: IdeState -> NormalizedFilePath -> Action (Maybe SemanticTokens) computeSemanticTokens state nfp = runMaybeT $ do - -- let dbg = logWith state + -- let dbg = logWith state Debug + let logError = logWith state Debug -- let getAst HAR{hieAst, refMap} = hieAst (HAR{hieAst, refMap, hieModule}, _) <- useWithStaleMT GetHieAst nfp (_, ast) <- MaybeT $ return $ listToMaybe $ Map.toList $ getAsts hieAst @@ -133,7 +134,7 @@ computeSemanticTokens state nfp = -- liftIO $ mapM_ (\x -> mapM_ (dbg . show) x) $ recoverSemanticTokens (bytestringString source) tokens pure tokens Left err -> do - liftIO $ putStrLn $ "computeSemanticTokens: " <> show err + logError $ "computeSemanticTokens: " <> show err MaybeT . pure $ Nothing where getType env n nameMap diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 3c03bdffd4..72f2648f42 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -23,13 +23,11 @@ import Language.LSP.Protocol.Types (LspEnum (knownValues), SemanticTokens (SemanticTokens), UInt, absolutizeTokens) + {- | 1. from our token type to LSP default token type -} -semanticTokenAbsoluteActualToken :: SemanticTokenAbsolute -> ActualToken -semanticTokenAbsoluteActualToken (SemanticTokenAbsolute line startChar len tokenType tokenModifiers) = - (line, startChar, len, fromLspTokenType tokenType, 0) -- mapping from our token type to LSP default token type toLspTokenType :: SemanticTokenType -> SemanticTokenTypes @@ -138,6 +136,8 @@ infoTokenType x = case x of ---- from lsp token to our token -------------------------------- +-- line, startChar, len, tokenType, modifiers +type ActualToken = (UInt, UInt, UInt, SemanticTokenType, UInt) -- | recoverSemanticTokens -- used for debug and test -- this function is used to recover the original tokens(with token in haskell token type zoon) @@ -158,10 +158,15 @@ recoverSemanticTokens sourceCode (SemanticTokens _ xs) = fmap (tokenOrigin sourc $ mapM fromTuple (chunksOf 5 $ map fromIntegral xs) where decodeError = Left "recoverSemanticTokenRelative: wrong token data" - fromTuple [a, b, c, d, _] = Just $ SemanticTokenRelative a b c (fromInt $ fromIntegral d) [] + fromTuple [a, b, c, d, _] = SemanticTokenRelative a b c <$> (fromInt $ fromIntegral d) <*> return [] fromTuple _ = Nothing + semanticTokenAbsoluteActualToken :: SemanticTokenAbsolute -> ActualToken + semanticTokenAbsoluteActualToken (SemanticTokenAbsolute line startChar len tokenType tokenModifiers) = + (line, startChar, len, fromLspTokenType tokenType, 0) + {- -} -- legends :: SemanticTokensLegend -fromInt :: Int -> SemanticTokenTypes -fromInt i = Set.elemAt i knownValues +-- fromInt i = Set.elemAt i knownValues +fromInt :: Int -> Maybe SemanticTokenTypes +fromInt i = Set.toAscList knownValues !? i diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 4c08e09551..4e2db43695 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -42,10 +42,6 @@ instance Semigroup SemanticTokenType where instance Monoid SemanticTokenType where mempty = TNothing - -type ActualToken = (UInt, UInt, UInt, SemanticTokenType, UInt) --- { line: 2, startChar 5, length: 3, tokenType: SemanticTokenType, tokenModifiers: 3, string}, - data SemanticTokenOriginal = SemanticTokenOriginal { tokenType :: SemanticTokenType, loc :: Loc, From ffb76b65d460f3dc83f8d64c365c6b2d7aaa25bd Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 16 Dec 2023 02:54:45 +0800 Subject: [PATCH 29/73] Refactor: toLspTokenType should return Maybe type --- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 31 ++++++++++--------- .../src/Ide/Plugin/SemanticTokens/Query.hs | 6 ++-- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 72f2648f42..6205a68424 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -6,12 +6,13 @@ This module contains the mapping 4. from lsp token to our token -} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} module Ide.Plugin.SemanticTokens.Mappings where import qualified Data.List as List import Data.List.Extra (chunksOf, (!?)) import qualified Data.Map as Map -import Data.Maybe (fromMaybe) +import Data.Maybe (fromMaybe, mapMaybe) import qualified Data.Set as Set import Data.Text (Text) import Development.IDE.GHC.Compat @@ -30,32 +31,32 @@ import Language.LSP.Protocol.Types (LspEnum (knownValues), -- mapping from our token type to LSP default token type -toLspTokenType :: SemanticTokenType -> SemanticTokenTypes +toLspTokenType :: SemanticTokenType -> Maybe SemanticTokenTypes toLspTokenType tk = case tk of -- TVariable -> SemanticTokenTypes_Variable -- left hand side of none pattern bind - TValBind -> SemanticTokenTypes_Function + TValBind -> Just SemanticTokenTypes_Function -- any pattern bind - TPatternBind -> SemanticTokenTypes_Parameter - TClass -> SemanticTokenTypes_Class - TClassMethod -> SemanticTokenTypes_Method - TTypeVariable -> SemanticTokenTypes_TypeParameter + TPatternBind -> Just SemanticTokenTypes_Parameter + TClass -> Just SemanticTokenTypes_Class + TClassMethod -> Just SemanticTokenTypes_Method + TTypeVariable -> Just SemanticTokenTypes_TypeParameter -- normal data type is a tagged union type look like enum type -- and a record is a product type like struct -- but we don't distinguish them yet - TTypeCon -> SemanticTokenTypes_Enum - TDataCon -> SemanticTokenTypes_EnumMember - TRecField -> SemanticTokenTypes_Property + TTypeCon -> Just SemanticTokenTypes_Enum + TDataCon -> Just SemanticTokenTypes_EnumMember + TRecField -> Just SemanticTokenTypes_Property -- pattern syn is like a limited version of macro of constructing a data type - TPatternSyn -> SemanticTokenTypes_Macro + TPatternSyn -> Just SemanticTokenTypes_Macro -- saturated type - TTypeSyn -> SemanticTokenTypes_Type + TTypeSyn -> Just SemanticTokenTypes_Type -- not sure if this is correct choice - TTypeFamily -> SemanticTokenTypes_Interface - TNothing -> SemanticTokenTypes_Namespace + TTypeFamily -> Just SemanticTokenTypes_Interface + TNothing -> Nothing lspTokenReverseMap :: Map.Map SemanticTokenTypes SemanticTokenType -lspTokenReverseMap = Map.fromList $ List.map (\x -> (toLspTokenType x, x)) $ enumFrom minBound +lspTokenReverseMap = Map.fromList $ mapMaybe (\x -> fmap (,x) (toLspTokenType x) ) $ enumFrom minBound fromLspTokenType :: SemanticTokenTypes -> SemanticTokenType fromLspTokenType tk = fromMaybe TNothing $ Map.lookup tk lspTokenReverseMap diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 7fc909e573..c7cedbb53e 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -122,19 +122,19 @@ semanticTokenAbsoluteSemanticTokens = makeSemanticTokens defaultSemanticTokensLe extractSemanticTokensFromNames :: NameSemanticMap -> [(Span, Name)] -> [SemanticTokenAbsolute] extractSemanticTokensFromNames nsm = - map (uncurry toAbsSemanticToken) . mergeNameFromSamSpan . mapMaybe (getSemantic nsm) + mapMaybe (uncurry toAbsSemanticToken) . mergeNameFromSamSpan . mapMaybe (getSemantic nsm) where -- merge all tokens with same span mergeNameFromSamSpan :: [(Span, SemanticTokenType)] -> [(Span, SemanticTokenType)] mergeNameFromSamSpan xs = Map.toList $ Map.fromListWith (<>) xs - toAbsSemanticToken :: Span -> SemanticTokenType -> SemanticTokenAbsolute + toAbsSemanticToken :: Span -> SemanticTokenType -> Maybe SemanticTokenAbsolute toAbsSemanticToken loc tokenType = let line = srcSpanStartLine loc - 1 startChar = srcSpanStartCol loc - 1 len = srcSpanEndCol loc - 1 - startChar in SemanticTokenAbsolute (fromIntegral line) (fromIntegral startChar) - (fromIntegral len) (toLspTokenType tokenType) [] + (fromIntegral len) <$> toLspTokenType tokenType <*> return [] -- SemanticTokenModifiers_Declaration getSemantic :: Map Name SemanticTokenType -> (Span, Name) -> Maybe (Span, SemanticTokenType) From 98b39dffc0f1a67c6f9667409b74a9dcc4c69fa9 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 16 Dec 2023 05:07:08 +0800 Subject: [PATCH 30/73] Stop use stale hieAst --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 5 +++-- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 10 ++++------ .../src/Ide/Plugin/SemanticTokens/Types.hs | 1 - 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 948fc302e4..28feb0bd7a 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -78,6 +78,7 @@ import Development.IDE.Types.Exports (ExportsMap (..), createExportsMapHieDb) import Development.IDE.Types.HscEnvEq (hscEnv) +import Development.IDE.Core.PluginUtils (useMT) import Ide.Plugin.SemanticTokens.Mappings logWith :: (MonadIO m) => IdeState -> Priority -> String -> m () @@ -103,7 +104,7 @@ computeSemanticTokens state nfp = -- let dbg = logWith state Debug let logError = logWith state Debug -- let getAst HAR{hieAst, refMap} = hieAst - (HAR{hieAst, refMap, hieModule}, _) <- useWithStaleMT GetHieAst nfp + (HAR{hieAst, refMap, hieModule}) <- useMT GetHieAst nfp (_, ast) <- MaybeT $ return $ listToMaybe $ Map.toList $ getAsts hieAst (TcModuleResult{..}, _) <- useWithStaleMT TypeCheck nfp (hscEnv -> hsc, _) <- useWithStaleMT GhcSessionDeps nfp @@ -158,7 +159,7 @@ semanticTokensFull state _ param = do case items of Nothing -> pure $ InR Null Just items -> do - content <- liftIO $ readFile $ fromNormalizedFilePath nfp + -- content <- liftIO $ readFile $ fromNormalizedFilePath nfp -- mapM_ (mapM_ (dbg . show)) $ recoverSemanticTokens content items pure $ InL items diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 6205a68424..7d0a442f07 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -159,15 +159,13 @@ recoverSemanticTokens sourceCode (SemanticTokens _ xs) = fmap (tokenOrigin sourc $ mapM fromTuple (chunksOf 5 $ map fromIntegral xs) where decodeError = Left "recoverSemanticTokenRelative: wrong token data" - fromTuple [a, b, c, d, _] = SemanticTokenRelative a b c <$> (fromInt $ fromIntegral d) <*> return [] + fromTuple [a, b, c, d, _] = SemanticTokenRelative a b c <$> fromInt (fromIntegral d) <*> return [] fromTuple _ = Nothing semanticTokenAbsoluteActualToken :: SemanticTokenAbsolute -> ActualToken semanticTokenAbsoluteActualToken (SemanticTokenAbsolute line startChar len tokenType tokenModifiers) = (line, startChar, len, fromLspTokenType tokenType, 0) -{- -} --- legends :: SemanticTokensLegend --- fromInt i = Set.elemAt i knownValues -fromInt :: Int -> Maybe SemanticTokenTypes -fromInt i = Set.toAscList knownValues !? i + -- legends :: SemanticTokensLegend + fromInt :: Int -> Maybe SemanticTokenTypes + fromInt i = Set.toAscList knownValues !? i diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 4e2db43695..8c71675318 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -15,7 +15,6 @@ import Development.IDE.GHC.Compat import Language.LSP.Protocol.Types --- data SemanticTokenType = SClass | SVariable | STypeVar | SDataCon | SNothing | SFieldName deriving (Eq, Ord) -- !!!! order of declarations matters deriving enum and ord -- since token may come from different source and we want to keep the most specific one -- and we might want to merge them. From ea5bc13922a56f9234dba9984f939bc6c1f361ac Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 16 Dec 2023 07:42:19 +0800 Subject: [PATCH 31/73] add getImportedNameSemanticRule rule to semantic tokens plugin --- .../hls-semantic-tokens-plugin.cabal | 2 + .../src/Ide/Plugin/SemanticTokens.hs | 6 +- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 83 ++++++++++--------- .../src/Ide/Plugin/SemanticTokens/Query.hs | 23 +++-- .../src/Ide/Plugin/SemanticTokens/Types.hs | 31 +++++-- .../hls-semantic-tokens-plugin/test/Main.hs | 5 +- src/HlsPlugins.hs | 2 +- 7 files changed, 89 insertions(+), 63 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal index 871956c1ea..9e5ff50942 100644 --- a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal +++ b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal @@ -47,6 +47,8 @@ library , transformers , bytestring , syb + , deepseq + , hls-graph == 2.5.0.0 default-language: Haskell2010 default-extensions: DataKinds diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index 9d327b03fd..4e175285a6 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -1,6 +1,7 @@ module Ide.Plugin.SemanticTokens (descriptor) where import Development.IDE +import Development.IDE.Core.Rules (Log) import qualified Ide.Plugin.SemanticTokens.Internal as Internal import qualified Ide.Plugin.SemanticTokens.Types as Types import Ide.Types @@ -8,8 +9,9 @@ import Language.LSP.Protocol.Message -descriptor :: PluginId -> PluginDescriptor IdeState -descriptor plId = (defaultPluginDescriptor plId) +descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState +descriptor recorder plId = (defaultPluginDescriptor plId) { Ide.Types.pluginHandlers = mkPluginHandler SMethod_TextDocumentSemanticTokensFull Internal.semanticTokensFull + , Ide.Types.pluginRules = Internal.getImportedNameSemanticRule recorder } diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 28feb0bd7a..8c0b26e9fb 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -1,10 +1,12 @@ {-# LANGUAGE DataKinds #-} +{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} module Ide.Plugin.SemanticTokens.Internal where @@ -13,8 +15,8 @@ import Control.Lens ((^.)) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Trans.Maybe (MaybeT (..)) import Data.Data (Data) -import Data.Generics (Typeable, everything, - mkQ) +import Data.Generics (Generic, Typeable, + everything, mkQ) import Data.List (sortBy) import qualified Data.List as List import Data.Maybe (catMaybes, fromMaybe, @@ -28,15 +30,19 @@ import Development.IDE (Action, GhcSessionDeps (GhcSessionDeps, GhcSessionDeps_), HieAstResult (HAR, hieAst, hieModule, refMap), IdeAction, IdeState, - Priority (..), + Priority (..), Recorder, + RuleResult, TypeCheck (TypeCheck), + WithPriority, catchSrcErrors, - ideLogger, logPriority, - realSpan, use, - useWithStaleFast, uses) + cmapWithPrio, ideLogger, + logPriority, realSpan, + use, useWithStaleFast, + uses) import Development.IDE.Core.Compile (TcModuleResult (..), lookupName) -import Development.IDE.Core.Rules (getSourceFileSource, +import Development.IDE.Core.Rules (Log (LogShake), + getSourceFileSource, runAction) import Development.IDE.Core.Shake (ShakeExtras (..), getShakeExtras, @@ -78,23 +84,23 @@ import Development.IDE.Types.Exports (ExportsMap (..), createExportsMapHieDb) import Development.IDE.Types.HscEnvEq (hscEnv) +import Development.IDE (Rules, define, + useWithStale_) import Development.IDE.Core.PluginUtils (useMT) +import Development.IDE.Core.Rules (Log) +import Development.IDE.Graph.Classes (Hashable, NFData (rnf)) import Ide.Plugin.SemanticTokens.Mappings logWith :: (MonadIO m) => IdeState -> Priority -> String -> m () logWith st prior = liftIO . logPriority (ideLogger st) prior. T.pack --- logWith :: (MonadIO m) => IdeState -> String -> m () --- logWith st = liftIO . print - - ----------------------- ---- the api ----------------------- -- | computeSemanticTokens -- collect from various source --- global names and imported name from typechecked and hscEnv +-- imported name from typechecked and hscEnv -- local names from refMap -- name locations from hieAst -- visible names from renamedSource @@ -104,48 +110,26 @@ computeSemanticTokens state nfp = -- let dbg = logWith state Debug let logError = logWith state Debug -- let getAst HAR{hieAst, refMap} = hieAst - (HAR{hieAst, refMap, hieModule}) <- useMT GetHieAst nfp - (_, ast) <- MaybeT $ return $ listToMaybe $ Map.toList $ getAsts hieAst + (HAR{hieAst, refMap}) <- useMT GetHieAst nfp (TcModuleResult{..}, _) <- useWithStaleMT TypeCheck nfp - (hscEnv -> hsc, _) <- useWithStaleMT GhcSessionDeps nfp + (_, ast) <- MaybeT $ return $ listToMaybe $ Map.toList $ getAsts hieAst + (GTTMap{getNameSemanticMap}, _) <- useWithStaleMT GetGlobalNameSemantic nfp -- because the names get from ast might contain derived name let nameSet = nameGetter tmrRenamed -- only take names we cares about from ast let names = hieAstSpanNames nameSet ast - -- ask hscEnv for none local types, some from typechecked can avoid asking hscEnv - km <- liftIO $ foldrM (getType hsc) (tcg_type_env tmrTypechecked) nameSet - -- let km = tcg_type_env tmrTypechecked - -- global name map from type typecheck and hscEnv - let globalAndImportedModuleNameSemanticMap = Map.fromList $ flip mapMaybe (Set.toList nameSet) $ \name -> do - ty <- lookupNameEnv km name - return (name, tyThingSemantic ty) + -- let globalAndImportedModuleNameSemanticMap = Map.fromList $ flip mapMaybe (Set.toList nameSet) $ \name -> do -- local name map from refMap - let localNameSemanticMap = toNameSemanticMap $ Map.filterWithKey (\k _ -> - either (const False) (`Set.member` nameSet) k) - refMap - let combineMap = Map.unionWith (<>) localNameSemanticMap globalAndImportedModuleNameSemanticMap - -- print all names - -- deriving method locate in the same position as the class name - -- liftIO $ mapM_ (\ (name, tokenType) -> dbg ("debug semanticMap: " <> showClearName name <> ":" <> show tokenType )) $ Map.toList importedModuleNameSemanticMap - -- liftIO $ mapM_ (\ (span, name) -> dbg ("debug names: " <> showClearName name <> ":" <> showCompactRealSrc span ) ) names + let localNameSemanticMap = toNameSemanticMap nameSet refMap + let combineMap = plusNameEnv_C (<>) localNameSemanticMap getNameSemanticMap let moduleAbsTks = extractSemanticTokensFromNames combineMap names case semanticTokenAbsoluteSemanticTokens moduleAbsTks of Right tokens -> do source :: ByteString <- lift $ getSourceFileSource nfp - -- liftIO $ mapM_ (\x -> mapM_ (dbg . show) x) $ recoverSemanticTokens (bytestringString source) tokens pure tokens Left err -> do logError $ "computeSemanticTokens: " <> show err MaybeT . pure $ Nothing - where - getType env n nameMap - | Nothing <- lookupNameEnv nameMap n - = do kind <- lookupKind env n - pure $ maybe nameMap (extendNameEnv nameMap n) kind - | otherwise = pure nameMap - lookupKind :: HscEnv -> Name -> IO (Maybe TyThing) - lookupKind env = fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env - semanticTokensFull :: PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull semanticTokensFull state _ param = do @@ -164,3 +148,22 @@ semanticTokensFull state _ param = do pure $ InL items +getImportedNameSemanticRule :: Recorder (WithPriority Log) -> Rules () +getImportedNameSemanticRule recorder = + define (cmapWithPrio LogShake recorder) $ \GetGlobalNameSemantic nfp -> do + (HAR{hieAst, refMap, hieModule}, _) <- useWithStale_ GetHieAst nfp + (TcModuleResult{..}, _) <- useWithStale_ TypeCheck nfp + (hscEnv -> hsc, _) <- useWithStale_ GhcSessionDeps nfp + let nameSet = nameGetter tmrRenamed + km <- liftIO $ foldrM (getType hsc (tcg_type_env tmrTypechecked)) emptyNameEnv $ nameSetElemsStable nameSet + return ([],Just (GTTMap $ fmap tyThingSemantic km)) + where + -- ignore one already in current module + getType env localMap n nameMap + | Nothing <- lookupNameEnv localMap n + = do tyThing <- lookupImported env n + pure $ maybe nameMap (extendNameEnv nameMap n) tyThing + | otherwise = pure nameMap + lookupImported :: HscEnv -> Name -> IO (Maybe TyThing) + lookupImported env = fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env + diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index c7cedbb53e..a5b7d6a42a 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -58,24 +58,24 @@ import Language.LSP.Protocol.Types --- | extract semantic tokens from RenamedSource ------------------------------------------------ -nameToCollect :: Name -> Set.Set Name -nameToCollect = Set.singleton +nameToCollect :: Name -> NameSet +nameToCollect = unitNameSet -nameGetter :: RenamedSource -> Set.Set Name -nameGetter = everything Set.union (Set.empty `mkQ` nameToCollect) +nameGetter :: RenamedSource -> NameSet +nameGetter = everything unionNameSet (emptyNameSet `mkQ` nameToCollect) -------------------------------------------- --- | construct definition map from HieAST a -------------------------------------------- -hieAstSpanNames :: Set.Set Name -> HieAST a -> [(Span, Name)] +hieAstSpanNames :: NameSet -> HieAST a -> [(Span, Name)] hieAstSpanNames nameSet ast = if null (nodeChildren ast) then getIds ast else concatMap (hieAstSpanNames nameSet) (nodeChildren ast) where getIds :: HieAST a -> [(Span, Name)] getIds ast = [(nodeSpan ast, c) | (Right c, d) <- Map.toList $ getNodeIds' ast - , Set.member c nameSet + , elemNameSet c nameSet -- at least get one info , not $ any isEvidenceBind $ identInfo d , not $ any isEvidenceUse $ identInfo d @@ -98,14 +98,13 @@ hieAstSpanNames nameSet ast = if null (nodeChildren ast) then --- | extract semantic tokens from NameSemanticMap -------------------------------------------------- -type NameSemanticMap = Map Name SemanticTokenType -toNameSemanticMap :: RefMap a -> NameSemanticMap -toNameSemanticMap rm = Map.fromListWith (<>) +toNameSemanticMap :: NameSet -> RefMap a -> NameSemanticMap +toNameSemanticMap ns rm = extendNameEnvList_C (<>) emptyNameEnv [ -- trace ("toNameSemanticMap" <> ":" <> showSDocUnsafe (ppr name) <> " : " <> showCompactRealSrc span <> ":" <> showIdentifierDetails detail <> " : " <> show tokenType) (name, tokenType) - | (Right name, details) <- Map.toList rm + | (name, Just details) <- map (\x -> (x, Map.lookup (Right x) rm)) $ nameSetElemsStable ns , not $ isDerivedOccName (occName name) , (span, detail) <- details , let tokenType = detailSemanticMaybeTokenType $ identInfo detail @@ -137,11 +136,11 @@ extractSemanticTokensFromNames nsm = (fromIntegral len) <$> toLspTokenType tokenType <*> return [] -- SemanticTokenModifiers_Declaration - getSemantic :: Map Name SemanticTokenType -> (Span, Name) -> Maybe (Span, SemanticTokenType) + getSemantic :: NameSemanticMap -> (Span, Name) -> Maybe (Span, SemanticTokenType) getSemantic nameMap (span, name) = do -- let tkt = toTokenType name -- let tokenType = maybe tkt (\x -> tkt <> x) $ Map.lookup name nameMap - let tokenType = Map.lookup name nameMap + let tokenType = lookupNameEnv nameMap name fmap (span,) tokenType diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 8c71675318..b4935e37ef 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -1,17 +1,24 @@ +{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE StrictData #-} +{-# LANGUAGE TypeFamilies #-} module Ide.Plugin.SemanticTokens.Types where -import qualified Data.List as List -import qualified Data.List.Extra as List -import qualified Data.List.NonEmpty as NE -import qualified Data.Map as Map -import Data.Maybe (fromMaybe) -import qualified Data.Set as Set +import qualified Data.List as List +import qualified Data.List.Extra as List +import qualified Data.List.NonEmpty as NE +import qualified Data.Map as Map +import Data.Maybe (fromMaybe) +import qualified Data.Set as Set import Development.IDE.GHC.Compat -- import GHC.Enum (boundedEnumFrom) +import Control.DeepSeq (NFData (rnf), rwhnf) +import Data.Generics (Typeable) +import Development.IDE (RuleResult) +import Development.IDE.Graph.Classes (Hashable) +import GHC.Generics (Generic) import Language.LSP.Protocol.Types @@ -56,3 +63,15 @@ data Loc = Loc deriving (Show, Eq, Ord) +type NameSemanticMap = NameEnv SemanticTokenType +data GetGlobalNameSemantic = GetGlobalNameSemantic + deriving (Eq, Show, Typeable, Generic) +instance Hashable GetGlobalNameSemantic +instance NFData GetGlobalNameSemantic + +data GlobalTokenTypeMap = GTTMap {getNameSemanticMap :: !NameSemanticMap } +instance NFData GlobalTokenTypeMap where + rnf (GTTMap a) = rwhnf a +instance Show GlobalTokenTypeMap where + show = const "GlobalNameMap" +type instance RuleResult GetGlobalNameSemantic = GlobalTokenTypeMap diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 27d780d41a..7315dea9ba 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -22,6 +22,7 @@ import qualified Data.Maybe import qualified Data.Set as Set import Data.String (fromString) import Data.Text +import Development.IDE.Core.Rules (Log) import Development.IDE.Plugin.Test (WaitForIdeRuleResult (..)) import Ide.Plugin.Error (getNormalizedFilePathE) import Ide.Plugin.SemanticTokens @@ -49,8 +50,8 @@ testDataDir = "test" "testdata" mkFs :: [FS.FileTree] -> FS.VirtualFileTree mkFs = FS.mkVirtualFileTree testDataDir -semanticTokensPlugin :: Test.Hls.PluginTestDescriptor () -semanticTokensPlugin = Test.Hls.mkPluginTestDescriptor' Ide.Plugin.SemanticTokens.descriptor "SemanticTokens" +semanticTokensPlugin :: Test.Hls.PluginTestDescriptor Log +semanticTokensPlugin = Test.Hls.mkPluginTestDescriptor Ide.Plugin.SemanticTokens.descriptor "SemanticTokens" mkSemanticTokensParams :: TextDocumentIdentifier -> SemanticTokensParams diff --git a/src/HlsPlugins.hs b/src/HlsPlugins.hs index 8ab78900c8..d97cda79fa 100644 --- a/src/HlsPlugins.hs +++ b/src/HlsPlugins.hs @@ -178,7 +178,7 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins CallHierarchy.descriptor "callHierarchy" : #endif #if hls_semanticTokens - SemanticTokens.descriptor "semanticTokens" : + let pId = "semanticTokens" in SemanticTokens.descriptor (pluginRecorder pId) pId: #endif #if hls_class let pId = "class" in Class.descriptor (pluginRecorder pId) pId: From 9439c4ad547b922ac4fc4c624b981e2180168ac9 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 16 Dec 2023 08:53:02 +0800 Subject: [PATCH 32/73] do not retrieve hie in getImportedNameSemanticRule --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 8c0b26e9fb..f747b2b5de 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -100,7 +100,7 @@ logWith st prior = liftIO . logPriority (ideLogger st) prior. T.pack -- | computeSemanticTokens -- collect from various source --- imported name from typechecked and hscEnv +-- imported name from GetGlobalNameSemantic -- local names from refMap -- name locations from hieAst -- visible names from renamedSource @@ -121,6 +121,7 @@ computeSemanticTokens state nfp = -- let globalAndImportedModuleNameSemanticMap = Map.fromList $ flip mapMaybe (Set.toList nameSet) $ \name -> do -- local name map from refMap let localNameSemanticMap = toNameSemanticMap nameSet refMap + let combineMap = plusNameEnv_C (<>) localNameSemanticMap getNameSemanticMap let moduleAbsTks = extractSemanticTokensFromNames combineMap names case semanticTokenAbsoluteSemanticTokens moduleAbsTks of @@ -151,15 +152,14 @@ semanticTokensFull state _ param = do getImportedNameSemanticRule :: Recorder (WithPriority Log) -> Rules () getImportedNameSemanticRule recorder = define (cmapWithPrio LogShake recorder) $ \GetGlobalNameSemantic nfp -> do - (HAR{hieAst, refMap, hieModule}, _) <- useWithStale_ GetHieAst nfp (TcModuleResult{..}, _) <- useWithStale_ TypeCheck nfp (hscEnv -> hsc, _) <- useWithStale_ GhcSessionDeps nfp let nameSet = nameGetter tmrRenamed - km <- liftIO $ foldrM (getType hsc (tcg_type_env tmrTypechecked)) emptyNameEnv $ nameSetElemsStable nameSet + km <- liftIO $ foldrM (getTypeExclude (tcg_type_env tmrTypechecked) hsc) emptyNameEnv $ nameSetElemsStable nameSet return ([],Just (GTTMap $ fmap tyThingSemantic km)) where -- ignore one already in current module - getType env localMap n nameMap + getTypeExclude localMap env n nameMap | Nothing <- lookupNameEnv localMap n = do tyThing <- lookupImported env n pure $ maybe nameMap (extendNameEnv nameMap n) tyThing From 7cc8b4402af4e3cfbe5805d88a34c002cb07cb9e Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 21 Dec 2023 01:43:38 +0800 Subject: [PATCH 33/73] fix: add description for semantic tokens --- .../src/Ide/Plugin/SemanticTokens.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index 4e175285a6..aaa88b9372 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE OverloadedStrings #-} module Ide.Plugin.SemanticTokens (descriptor) where import Development.IDE @@ -10,7 +11,7 @@ import Language.LSP.Protocol.Message descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState -descriptor recorder plId = (defaultPluginDescriptor plId) +descriptor recorder plId = (defaultPluginDescriptor plId "provides lsp semantic tokens features") { Ide.Types.pluginHandlers = mkPluginHandler SMethod_TextDocumentSemanticTokensFull Internal.semanticTokensFull , Ide.Types.pluginRules = Internal.getImportedNameSemanticRule recorder From 0f0a895441fce2ff5b60866bfb76f0e52aa094de Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 21 Dec 2023 12:48:43 +0800 Subject: [PATCH 34/73] remove TValBind and TPaternBind and Use TFunction and TVariable instead --- .../src/Ide/Plugin/SemanticTokens.hs | 2 + .../src/Ide/Plugin/SemanticTokens/Internal.hs | 28 +- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 39 +- .../src/Ide/Plugin/SemanticTokens/Query.hs | 51 +- .../src/Ide/Plugin/SemanticTokens/Types.hs | 4 +- .../src/Ide/Plugin/SemanticTokens/Utils.hs | 42 +- .../hls-semantic-tokens-plugin/test/Main.hs | 713 +++++++++++------- .../test/testdata/TFunction.hs | 7 + .../test/testdata/TFunctionLet.hs | 4 + .../test/testdata/TFunctionLocal.hs | 8 + .../test/testdata/TPatternbind.hs | 2 +- 11 files changed, 577 insertions(+), 323 deletions(-) create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.hs create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.hs diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index aaa88b9372..80b4a9fcda 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -16,3 +16,5 @@ descriptor recorder plId = (defaultPluginDescriptor plId "provides lsp semantic mkPluginHandler SMethod_TextDocumentSemanticTokensFull Internal.semanticTokensFull , Ide.Types.pluginRules = Internal.getImportedNameSemanticRule recorder } + + diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index f747b2b5de..4a61dd1547 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -6,6 +6,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} @@ -84,12 +85,15 @@ import Development.IDE.Types.Exports (ExportsMap (..), createExportsMapHieDb) import Development.IDE.Types.HscEnvEq (hscEnv) +import qualified Data.Map as M +import qualified Data.Set as S import Development.IDE (Rules, define, useWithStale_) import Development.IDE.Core.PluginUtils (useMT) import Development.IDE.Core.Rules (Log) import Development.IDE.Graph.Classes (Hashable, NFData (rnf)) import Ide.Plugin.SemanticTokens.Mappings +import Ide.Plugin.SemanticTokens.Utils logWith :: (MonadIO m) => IdeState -> Priority -> String -> m () logWith st prior = liftIO . logPriority (ideLogger st) prior. T.pack @@ -101,29 +105,25 @@ logWith st prior = liftIO . logPriority (ideLogger st) prior. T.pack -- | computeSemanticTokens -- collect from various source -- imported name from GetGlobalNameSemantic --- local names from refMap +-- local names from hieAst -- name locations from hieAst -- visible names from renamedSource computeSemanticTokens :: IdeState -> NormalizedFilePath -> Action (Maybe SemanticTokens) computeSemanticTokens state nfp = runMaybeT $ do - -- let dbg = logWith state Debug let logError = logWith state Debug - -- let getAst HAR{hieAst, refMap} = hieAst (HAR{hieAst, refMap}) <- useMT GetHieAst nfp + typedAst :: HieASTs Type <- MaybeT $ pure $ cast hieAst (TcModuleResult{..}, _) <- useWithStaleMT TypeCheck nfp - (_, ast) <- MaybeT $ return $ listToMaybe $ Map.toList $ getAsts hieAst + (_, ast) <- MaybeT $ return $ listToMaybe $ Map.toList $ getAsts typedAst (GTTMap{getNameSemanticMap}, _) <- useWithStaleMT GetGlobalNameSemantic nfp -- because the names get from ast might contain derived name let nameSet = nameGetter tmrRenamed - -- only take names we cares about from ast - let names = hieAstSpanNames nameSet ast - -- let globalAndImportedModuleNameSemanticMap = Map.fromList $ flip mapMaybe (Set.toList nameSet) $ \name -> do - -- local name map from refMap - let localNameSemanticMap = toNameSemanticMap nameSet refMap - - let combineMap = plusNameEnv_C (<>) localNameSemanticMap getNameSemanticMap - let moduleAbsTks = extractSemanticTokensFromNames combineMap names + let spanNames = hieAstSpanNames nameSet ast + let localNameSemanticMapFromIdDetails = toNameSemanticMap nameSet refMap + let localNameSemanticMapFromTypes = fmap typeSemantic $ mkNameEnv $ getNameTypes ast refMap + let combineMap = plusNameEnv_C (<>) localNameSemanticMapFromTypes $ plusNameEnv_C (<>) localNameSemanticMapFromIdDetails getNameSemanticMap + let moduleAbsTks = extractSemanticTokensFromNames combineMap spanNames case semanticTokenAbsoluteSemanticTokens moduleAbsTks of Right tokens -> do source :: ByteString <- lift $ getSourceFileSource nfp @@ -155,8 +155,8 @@ getImportedNameSemanticRule recorder = (TcModuleResult{..}, _) <- useWithStale_ TypeCheck nfp (hscEnv -> hsc, _) <- useWithStale_ GhcSessionDeps nfp let nameSet = nameGetter tmrRenamed - km <- liftIO $ foldrM (getTypeExclude (tcg_type_env tmrTypechecked) hsc) emptyNameEnv $ nameSetElemsStable nameSet - return ([],Just (GTTMap $ fmap tyThingSemantic km)) + tm <- liftIO $ foldrM (getTypeExclude (tcg_type_env tmrTypechecked) hsc) emptyNameEnv $ nameSetElemsStable nameSet + return ([],Just (GTTMap $ fmap tyThingSemantic $ plusNameEnv (tcg_type_env tmrTypechecked) tm)) where -- ignore one already in current module getTypeExclude localMap env n nameMap diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 7d0a442f07..2dd31a8736 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -35,9 +35,8 @@ toLspTokenType :: SemanticTokenType -> Maybe SemanticTokenTypes toLspTokenType tk = case tk of -- TVariable -> SemanticTokenTypes_Variable -- left hand side of none pattern bind - TValBind -> Just SemanticTokenTypes_Function - -- any pattern bind - TPatternBind -> Just SemanticTokenTypes_Parameter + TFunction -> Just SemanticTokenTypes_Function + TVariable -> Just SemanticTokenTypes_Variable TClass -> Just SemanticTokenTypes_Class TClassMethod -> Just SemanticTokenTypes_Method TTypeVariable -> Just SemanticTokenTypes_TypeParameter @@ -66,12 +65,29 @@ fromLspTokenType tk = fromMaybe TNothing $ Map.lookup tk lspTokenReverseMap 2. from ghc type (TyThing or name) to our token type -} +typeSemantic :: Type -> SemanticTokenType +typeSemantic x = + case x of + ForAllTy _ a -> typeSemantic a + FunTy _ _ -> TFunction + TyVarTy _ -> TNothing + TyConApp tyCon _ -> TNothing + -- | isTypeSynonymTyCon tyCon -> TTypeSyn + -- | isTypeFamilyTyCon tyCon -> TTypeFamily + -- | isClassTyCon tyCon -> TClass + -- | otherwise -> TTypeCon + AppTy a b -> TNothing + LitTy _ -> TNothing + CastTy _ _ -> TNothing + CoercionTy _ -> TNothing + + toTokenType :: Name -> SemanticTokenType toTokenType locName = case occNameSpace $ occName locName of x | isDataConNameSpace x -> TDataCon x | isTvNameSpace x -> TTypeVariable x | isTcClsNameSpace x -> TTypeCon -- Type constructors and classes in the same name space for now - x | isVarNameSpace x -> TValBind + x | isVarNameSpace x -> TVariable _ -> TNothing @@ -83,9 +99,9 @@ tyThingSemantic ty = case ty of | isTyVar vid -> TTypeVariable | isRecordSelector vid -> TRecField | isClassOpId vid -> TClassMethod - -- | isLocalId vid -> TPatternBind + | isFunVar vid -> TFunction -- | isDFunId vid -> TClassMethod - | otherwise -> TValBind + | otherwise -> TVariable AConLike con -> case con of RealDataCon _ -> TDataCon PatSynCon _ -> TPatternSyn @@ -100,6 +116,13 @@ tyThingSemantic ty = case ty of | otherwise -> TTypeCon ACoAxiom _ -> TNothing +isFunVar :: Var -> Bool +isFunVar var = isFun $ varType var +isFun :: Type -> Bool +isFun a = case a of + ForAllTy _ a -> isFun a + x -> isFunTy a + {- | 3. from hieAst identifier detail to our token type @@ -113,9 +136,9 @@ infoTokenType x = case x of IEThing _ -> TNothing TyDecl -> TNothing -- type signature - ValBind RegularBind _ _ -> TValBind + ValBind RegularBind _ _ -> TVariable ValBind InstanceBind _ _ -> TClassMethod - PatternBind {} -> TPatternBind + PatternBind {} -> TVariable ClassTyDecl _ -> TClassMethod TyVarBind _ _ -> TTypeVariable RecField _ _ -> TRecField diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index a5b7d6a42a..76062e94ed 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -9,7 +9,7 @@ module Ide.Plugin.SemanticTokens.Query where import Control.Arrow (ArrowChoice ((|||)), (&&&)) -import Control.Monad (forM) +import Control.Monad (foldM, forM) import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Char (isAlphaNum) import Data.Either (rights) @@ -21,7 +21,8 @@ import qualified Data.List.NonEmpty as NE import Data.Map (Map) import qualified Data.Map as Map import Data.Maybe (catMaybes, fromMaybe, - listToMaybe, mapMaybe) + listToMaybe, mapMaybe, + maybeToList) import Data.Ord (comparing) import qualified Data.Set as Set import Data.Text (Text) @@ -42,6 +43,9 @@ import Development.IDE.Types.Location (toNormalizedFilePath') import Development.IDE.Types.Shake (WithHieDb) import Generics.SYB (mkQ) -- import HieDb.Types (DefRow (..)) +import qualified Data.Map as M +import Data.Monoid (First (..)) +import qualified Data.Set as S import HieDb (DefRow (..), HieDbErr (AmbiguousUnitId, NameNotFound, NameUnhelpfulSpan, NoNameAtPoint, NotIndexed), ModuleInfo (modInfoSrcFile), @@ -51,6 +55,7 @@ import HieDb (DefRow (..), type (:.) (..)) import Ide.Plugin.SemanticTokens.Mappings import Ide.Plugin.SemanticTokens.Types +import Ide.Plugin.SemanticTokens.Utils (showName) import Language.LSP.Protocol.Types @@ -64,9 +69,45 @@ nameToCollect = unitNameSet nameGetter :: RenamedSource -> NameSet nameGetter = everything unionNameSet (emptyNameSet `mkQ` nameToCollect) --------------------------------------------- ---- | construct definition map from HieAST a --------------------------------------------- +-------------------------------------------------- +--- | extract location and type info from HieAST a +-------------------------------------------------- + +-- todo this is O(n^2) but we might do way better, +getNameTypes :: HieAST Type -> RefMap a -> [(Name, Type)] +getNameTypes ast rm = mapMaybe (nameType ast rm) ns + where ns = rights $ M.keys rm + +nameType :: HieAST Type -> RefMap a -> Name -> Maybe (Name,Type) +nameType hie rm ns = do + span <- -- traceShow ("to find Name", showName ns) $ + getDefinitionSite rm ns + ty <- -- traceShow ("nameBindSite", fmap typeSemantic $ spanTypeFromHie hie span, showName ns, span) $ + spanTypeFromHie hie span + return (ns, ty) + where + getDefinitionSite :: RefMap a -> Name -> Maybe Span + getDefinitionSite rm name = do + let nameString = showName name + xs <- -- traceShow ("getting spans:", nameString) $ + Map.lookup (Right name) rm + x <- -- traceShow ("getDefinitionSite:size:", nameString, length xs) $ + listToMaybe xs + let infos = concatMap (S.toList . identInfo. snd) xs + -- traceShow ("getDefinitionSite:infos", nameString, infos, mapMaybe getBindSiteFromContext infos) listToMaybe $ mapMaybe getBindSiteFromContext infos + listToMaybe $ mapMaybe getBindSiteFromContext infos + spanTypeFromHie :: HieAST Type -> Span -> Maybe Type + spanTypeFromHie ast span = do + ast <- selectSmallestContaining span ast + getNodeIType ast + getNodeIType hie = do + nodeInfo <- Map.lookup SourceInfo $ getSourcedNodeInfo $ sourcedNodeInfo hie + nt <- safeHead $ nodeType nodeInfo + return nt + safeHead :: [a] -> Maybe a + safeHead [] = Nothing + safeHead (x:_) = Just x + hieAstSpanNames :: NameSet -> HieAST a -> [(Span, Name)] hieAstSpanNames nameSet ast = if null (nodeChildren ast) then diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index b4935e37ef..4014402a2b 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -29,8 +29,8 @@ data SemanticTokenType = TNothing -- unknown -- | TVariable -- fallback -- since many thing can be valbind. we put it as less priority - | TValBind -- valBind instance bind or regular bind - | TPatternBind -- PatternBind, parameters, as values in let/in, case, lambda + | TVariable -- valBind instance bind or regular bind + | TFunction -- function | TDataCon -- Data constructor | TTypeVariable -- Type variable | TClassMethod -- Class method diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs index efe94772e6..90a967d527 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs @@ -1,12 +1,15 @@ -- stand alone deriving {-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeFamilies #-} + module Ide.Plugin.SemanticTokens.Utils where -import Data.ByteString (ByteString) -import Data.ByteString.Char8 (unpack) -import qualified Data.Map as Map +import Data.ByteString (ByteString) +import Data.ByteString.Char8 (unpack) +import qualified Data.Map as Map import Development.IDE.GHC.Compat +import Ide.Plugin.SemanticTokens.Types deriving instance Show DeclType deriving instance Show BindType @@ -18,7 +21,7 @@ instance Show ContextInfo where MatchBind -> "MatchBind" IEThing _ -> "IEThing IEType" -- imported TyDecl -> "TyDecl" - ValBind bt _ _ -> "ValBind of " <> show bt + ValBind bt _ span -> "ValBind of " <> show bt <> show span PatternBind {} -> "PatternBind" ClassTyDecl _ -> "ClassTyDecl" Decl d _ -> "Decl of " <> show d @@ -53,6 +56,9 @@ showLocatedNames xs = unlines showClearName :: Name -> String showClearName name = occNameString (occName name) <> ":" <> showSDocUnsafe (ppr name) <> ":" <> showNameType name +showName :: Name -> String +showName name = showSDocUnsafe (ppr name) <> ":" <> showNameType name + showNameType :: Name -> String showNameType name | isInternalName name = "InternalName" @@ -62,3 +68,31 @@ showNameType name bytestringString :: ByteString -> String bytestringString = map (toEnum . fromEnum) . unpack + +spanNamesString :: [(Span, Name)] -> String +spanNamesString xs = unlines + [ showSDocUnsafe (ppr span) ++ " " ++ showSDocUnsafe (ppr name) + | (span, name) <- xs] + +nameTypesString :: [(Name, Type)] -> String +nameTypesString xs = unlines + [ showSDocUnsafe (ppr span) ++ " " ++ showSDocUnsafe (ppr name) + | (span, name) <- xs] + + +nameMapString :: NameSemanticMap -> [Name] -> String +nameMapString nsm names = unlines + [ showSDocUnsafe (ppr name) ++ " " ++ show tokenType + | name <- names + , let tokenType = lookupNameEnv nsm name + ] + + +showSpan :: RealSrcSpan -> String +showSpan x = show (srcSpanStartLine x) <> ":" <> show (srcSpanStartCol x) <> "-" <> show (srcSpanEndCol x) + + + +type family Foo a where + Foo Int = Int + Foo a = String diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 7315dea9ba..81b0a71014 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -4,8 +4,6 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} - - import Control.Arrow (Arrow ((***)), (&&&), (+++)) import Control.Lens hiding (use) @@ -21,7 +19,7 @@ import Data.Maybe (fromJust) import qualified Data.Maybe import qualified Data.Set as Set import Data.String (fromString) -import Data.Text +import Data.Text hiding (length) import Development.IDE.Core.Rules (Log) import Development.IDE.Plugin.Test (WaitForIdeRuleResult (..)) import Ide.Plugin.Error (getNormalizedFilePathE) @@ -43,7 +41,6 @@ import Test.Hls import qualified Test.Hls.FileSystem as FS import Test.Hls.Util (withCanonicalTempDir) - testDataDir :: FilePath testDataDir = "test" "testdata" @@ -53,244 +50,287 @@ mkFs = FS.mkVirtualFileTree testDataDir semanticTokensPlugin :: Test.Hls.PluginTestDescriptor Log semanticTokensPlugin = Test.Hls.mkPluginTestDescriptor Ide.Plugin.SemanticTokens.descriptor "SemanticTokens" - mkSemanticTokensParams :: TextDocumentIdentifier -> SemanticTokensParams mkSemanticTokensParams = SemanticTokensParams Nothing Nothing runSessionWithServerInDir file x = - Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do - doc <- openDoc file "haskell" - res <- waitForAction "TypeCheck" doc - x doc + Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do + doc <- openDoc file "haskell" + res <- waitForAction "TypeCheck" doc + x doc runSessionWithServerInDirAndGetSemantic file x = - Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do - doc <- openDoc file "haskell" - res <- waitForAction "TypeCheck" doc - res <- Test.getSemanticTokens doc - x res doc + Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do + doc <- openDoc file "haskell" + res <- waitForAction "TypeCheck" doc + res <- Test.getSemanticTokens doc + x res doc runSessionWithServerInDirAndGetSemanticsFile file x = - Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do - let path = "." testDataDir file - content <- liftIO $ readFile path - doc <- openDoc file "haskell" - res <- waitForAction "TypeCheck" doc - res <- Test.getSemanticTokens doc - x res doc content + do + let path = "." testDataDir file + content <- liftIO $ readFile path + Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do + doc <- openDoc file "haskell" + res <- waitForAction "TypeCheck" doc + res <- Test.getSemanticTokens doc + x res doc content semanticTokensImportedTests :: TestTree -semanticTokensImportedTests = testGroup "imported test" - [ - testCase "type class" $ do - let expect = - [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} - , SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} - , SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 26, len = 4}, name = "Show"} - , SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 6, startChar = 1, len = 2}, name = "eq"} - , SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 6, startChar = 8, len = 2}, name = "Eq"} - , SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 11, len = 1}, name = "a"} - , SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 17, len = 1}, name = "a"} - , SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 22, len = 4}, name = "Bool"} - , SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 7, startChar = 1, len = 2}, name = "eq"} - , SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 7, startChar = 4, len = 1}, name = "b"} - , SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 7, startChar = 8, len = 1}, name = "b"} - , SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 7, startChar = 10, len = 2}, name = "=="} - , SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 7, startChar = 13, len = 1}, name = "b"}] - - - runSessionWithServerInDirAndGetSemanticsFile "TClass.hs" $ \res doc content -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" - - - +semanticTokensImportedTests = + testGroup + "imported test" + [ testCase "type class" $ do + let expect = + [ SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 26, len = 4}, name = "Show"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 1, len = 2}, name = "eq"}, + SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 6, startChar = 8, len = 2}, name = "Eq"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 11, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 17, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 22, len = 4}, name = "Bool"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 7, startChar = 1, len = 2}, name = "eq"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 7, startChar = 4, len = 1}, name = "b"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 7, startChar = 8, len = 1}, name = "b"}, + SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 7, startChar = 10, len = 2}, name = "=="}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 7, startChar = 13, len = 1}, name = "b"} + ] + + runSessionWithServerInDirAndGetSemanticsFile "TClass.hs" $ \res doc content -> do + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found" ] semanticTokensClassTests :: TestTree -semanticTokensClassTests = testGroup "type class" +semanticTokensClassTests = + testGroup + "type class" [ testCase "type class" $ do let filePath = "./test/testdata/TClass.hs" content <- liftIO $ Prelude.readFile filePath let expect = - [SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 7, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 4, startChar = 11, len = 1}, name = "a"} - ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 3, len = 3}, name = "foo"} - ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 10, len = 1}, name = "a"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 15, len = 3}, name = "Int"}] + [ SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 7, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 4, startChar = 11, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 3, len = 3}, name = "foo"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 10, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 15, len = 3}, name = "Int"} + ] runSessionWithServerInDirAndGetSemantic "TClass.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _notoken -> error "No tokens found" - , testCase "imported class method InstanceClassMethodBind" $ do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _notoken -> error "No tokens found", + testCase "imported class method InstanceClassMethodBind" $ do let filePath = "./test/testdata/TInstanceClassMethodBind.hs" content <- liftIO $ Prelude.readFile filePath let expect = - [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 16, len = 3}, name = "Int"} - ,SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 5, startChar = 10, len = 2}, name = "Eq"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 6, startChar = 5, len = 4}, name = "(==)"} - ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 6, startChar = 12, len = 9}, name = "undefined"}] + [ SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 16, len = 3}, name = "Int"}, + SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 5, startChar = 10, len = 2}, name = "Eq"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 6, startChar = 5, len = 4}, name = "(==)"}, + SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 12, len = 9}, name = "undefined"} + ] runSessionWithServerInDirAndGetSemantic "TInstanceClassMethodBind.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" - , - testCase "imported class method TInstanceClassMethodUse" $ do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found", + testCase "imported class method TInstanceClassMethodUse" $ do let filePath = "./test/testdata/TInstanceClassMethodUse.hs" content <- liftIO $ Prelude.readFile filePath - let expect = [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 2}, name = "go"} - ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 4, startChar = 9, len = 4}, name = "(==)"}] + let expect = + [ SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 2}, name = "go"}, + SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 4, startChar = 9, len = 4}, name = "(==)"} + ] runSessionWithServerInDirAndGetSemantic "TInstanceClassMethodUse.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" - - , testCase "imported deriving" $ do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either (error . show) (\xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found", + testCase "imported deriving" $ do let filePath = "./test/testdata/TClassImportedDeriving.hs" content <- liftIO $ Prelude.readFile filePath let expect = - [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 26, len = 4}, name = "Show"}] + [ SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 26, len = 4}, name = "Show"} + ] runSessionWithServerInDirAndGetSemantic "TClassImportedDeriving.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found" ] semanticTokensValuePatternTests :: TestTree semanticTokensValuePatternTests = testGroup - "value and patterns " - [ - testCase "value bind" $ do + "value and patterns " + [ testCase "value bind" $ do let filePath = "./test/testdata/TValBind.hs" content <- liftIO $ Prelude.readFile filePath let expect = - [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 5}, name = "hello"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 10, len = 3}, name = "Int"} - ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 5}, name = "hello"} - ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 9, len = 6}, name = "length"}] + [ SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 1, len = 5}, name = "hello"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 10, len = 3}, name = "Int"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 1, len = 5}, name = "hello"}, + SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 9, len = 6}, name = "length"} + ] runSessionWithServerInDirAndGetSemantic "TValBind.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _noTokens -> error "No tokens found" - , testCase "pattern match" $ do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _noTokens -> error "No tokens found", + testCase "pattern match" $ do let filePath = "./test/testdata/TPatternMatch.hs" content <- liftIO $ Prelude.readFile filePath - let expect = [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 4, startChar = 1, len = 1}, name = "g"} - ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 4, len = 7}, name = "Nothing"}] + let expect = + [ SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "g"}, + SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 4, len = 7}, name = "Nothing"} + ] runSessionWithServerInDirAndGetSemantic "TPatternMatch.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _noTokens -> error "No tokens found" - , testCase "pattern bind" $ do - let filePath = "./test/testdata/TPatternBind.hs" + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _noTokens -> error "No tokens found", + testCase "pattern bind" $ do + let filePath = "./test/testdata/TPatternbind.hs" content <- liftIO $ Prelude.readFile filePath let expect = - - [SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 3, startChar = 2, len = 1}, name = "a"} - ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 3, startChar = 5, len = 1}, name = "b"} - ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 1}, name = "f"} - ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 5, startChar = 3, len = 1}, name = "x"} - ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 5, startChar = 5, len = 1}, name = "y"} - ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 5, startChar = 9, len = 1}, name = "x"} - ,SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 11, len = 1}, name = "+"} - ,SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 5, startChar = 13, len = 1}, name = "y"}] - runSessionWithServerInDirAndGetSemantic "TPatternBind.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ forM (Prelude.zip xs expect) $ \(x, y) -> do - x @?= y - return ()) $ recoverSemanticTokens content tokens + [ SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 3, startChar = 2, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 3, startChar = 5, len = 1}, name = "b"}, + SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 5, startChar = 1, len = 1}, name = "f"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 3, len = 1}, name = "x"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 5, len = 1}, name = "y"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 9, len = 1}, name = "x"}, + SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 11, len = 1}, name = "+"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 13, len = 1}, name = "y"} + ] + runSessionWithServerInDirAndGetSemantic "TPatternbind.hs" $ \res doc -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ length xs @?= length expect) + $ recoverSemanticTokens content tokens + either + (error . show) + ( \xs -> liftIO $ forM (Prelude.zip xs expect) $ \(x, y) -> do + x @?= y return () - _notokens -> error "No tokens found" - ] + ) + $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found" + ] semanticTokensTests :: TestTree semanticTokensTests = testGroup - "other semantic Token test" - [ - testCase "mixed constancy test" $ do - let filePath = "./test/testdata/T1.hs" - content <- liftIO $ Prelude.readFile filePath - let expect = [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 18, len = 3}, name = "Set"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 6, startChar = 23, len = 6}, name = "insert"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 6, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 9, startChar = 12, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 9, startChar = 18, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 25, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 11, startChar = 7, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 11, startChar = 11, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 12, startChar = 3, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 10, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 15, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 14, startChar = 10, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 14, startChar = 14, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 5, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 15, startChar = 9, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 15, startChar = 13, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 15, len = 1}, name = "+"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 6, len = 2}, name = "Dd"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 17, startChar = 11, len = 2}, name = "Dd"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 14, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 19, startChar = 9, len = 3}, name = "One"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 19, startChar = 15, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 21, startChar = 1, len = 3}, name = "ggg"},SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 21, startChar = 7, len = 3}, name = "One"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 6, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 23, startChar = 12, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 16, len = 11}, name = "Prelude.Int"},SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 24, startChar = 6, len = 4}, name = "Bar1"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 24, startChar = 13, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 25, startChar = 6, len = 4}, name = "Bar2"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 25, startChar = 13, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 27, startChar = 1, len = 2}, name = "bb"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 27, startChar = 8, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 12, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 18, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 23, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 28, startChar = 1, len = 2}, name = "bb"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 28, startChar = 4, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 28, startChar = 9, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 28, startChar = 13, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 29, startChar = 1, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 7, len = 4}, name = "cool"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 29, startChar = 15, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 22, len = 4}, name = "cool"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 30, startChar = 1, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 4, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 9, len = 1}, name = "c"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 30, startChar = 14, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 17, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 30, startChar = 19, len = 1}, name = "c"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 31, startChar = 12, len = 2}, name = "xx"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 31, startChar = 16, len = 2}, name = "yy"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 32, startChar = 11, len = 2}, name = "dd"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 34, startChar = 2, len = 2}, name = "zz"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 34, startChar = 6, len = 2}, name = "kk"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 35, startChar = 1, len = 2}, name = "cc"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 7, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 15, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 20, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 28, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 36, startChar = 1, len = 2}, name = "cc"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 36, startChar = 4, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 36, startChar = 7, len = 2}, name = "gg"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 36, startChar = 11, len = 2}, name = "vv"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 37, startChar = 10, len = 2}, name = "gg"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 14, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 38, startChar = 18, len = 1}, name = "$"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 38, startChar = 20, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 24, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 14, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 39, startChar = 18, len = 1}, name = "$"},SemanticTokenOriginal {tokenType = TPatternBind, loc = Loc {line = 39, startChar = 20, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 24, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 41, startChar = 1, len = 2}, name = "go"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 41, startChar = 6, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 42, startChar = 1, len = 3}, name = "add"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 42, startChar = 7, len = 11}, name = "(Prelude.+)"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 47, startChar = 1, len = 4}, name = "main"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 47, startChar = 9, len = 2}, name = "IO"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 48, startChar = 1, len = 4}, name = "main"},SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 48, startChar = 8, len = 8}, name = "putStrLn"}] - runSessionWithServerInDirAndGetSemantic "T1.hs" $ \res doc -> do - case res ^? _L of - Just tokens -> do - either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" - , testCase "pattern syn" $ do + "other semantic Token test" + [ -- testCase "mixed constancy test result generated from one ghc version" $ do + -- let filePath = "./test/testdata/T1.hs" + -- content <- liftIO $ Prelude.readFile filePath + -- let expect = [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 18, len = 3}, name = "Set"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 23, len = 6}, name = "insert"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 6, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 9, startChar = 12, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 9, startChar = 18, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 25, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 11, startChar = 7, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 11, startChar = 11, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 12, startChar = 3, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 10, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 15, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 14, startChar = 10, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 14, startChar = 14, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 5, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 15, startChar = 9, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 15, startChar = 13, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 15, len = 1}, name = "+"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 6, len = 2}, name = "Dd"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 17, startChar = 11, len = 2}, name = "Dd"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 14, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 19, startChar = 9, len = 3}, name = "One"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 19, startChar = 15, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 21, startChar = 1, len = 3}, name = "ggg"},SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 21, startChar = 7, len = 3}, name = "One"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 6, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 23, startChar = 12, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 16, len = 11}, name = "Prelude.Int"},SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 24, startChar = 6, len = 4}, name = "Bar1"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 24, startChar = 13, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 25, startChar = 6, len = 4}, name = "Bar2"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 25, startChar = 13, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 27, startChar = 1, len = 2}, name = "bb"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 27, startChar = 8, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 12, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 18, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 23, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 28, startChar = 1, len = 2}, name = "bb"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 28, startChar = 4, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 28, startChar = 9, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 28, startChar = 13, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 29, startChar = 1, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 7, len = 4}, name = "cool"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 29, startChar = 15, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 22, len = 4}, name = "cool"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 1, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 4, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 9, len = 1}, name = "c"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 14, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 17, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 19, len = 1}, name = "c"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 31, startChar = 12, len = 2}, name = "xx"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 31, startChar = 16, len = 2}, name = "yy"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 32, startChar = 11, len = 2}, name = "dd"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 34, startChar = 2, len = 2}, name = "zz"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 34, startChar = 6, len = 2}, name = "kk"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 35, startChar = 1, len = 2}, name = "cc"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 7, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 15, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 20, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 28, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 36, startChar = 1, len = 2}, name = "cc"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 36, startChar = 4, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 36, startChar = 7, len = 2}, name = "gg"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 36, startChar = 11, len = 2}, name = "vv"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 37, startChar = 10, len = 2}, name = "gg"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 14, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 38, startChar = 18, len = 1}, name = "$"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 38, startChar = 20, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 24, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 14, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 39, startChar = 18, len = 1}, name = "$"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 39, startChar = 20, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 24, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 41, startChar = 1, len = 2}, name = "go"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 41, startChar = 6, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 42, startChar = 1, len = 3}, name = "add"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 42, startChar = 7, len = 11}, name = "(Prelude.+)"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 47, startChar = 1, len = 4}, name = "main"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 47, startChar = 9, len = 2}, name = "IO"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 48, startChar = 1, len = 4}, name = "main"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 48, startChar = 8, len = 8}, name = "putStrLn"}] + -- runSessionWithServerInDirAndGetSemantic "T1.hs" $ \res doc -> do + -- case res ^? _L of + -- Just tokens -> do + -- either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens + -- return () + -- _notokens -> error "No tokens found" + testCase "pattern syn" $ do let filePath = "./test/testdata/TPatternSyn.hs" content <- liftIO $ Prelude.readFile filePath - let expect = [SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 5, startChar = 9, len = 3} - , name = "Foo"}] + let expect = + [ SemanticTokenOriginal + { tokenType = TPatternSyn, + loc = Loc {line = 5, startChar = 9, len = 3}, + name = "Foo" + } + ] runSessionWithServerInDirAndGetSemantic "TPatternSyn.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" - - , testCase "type family" $ do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found", + testCase "type family" $ do let filePath = "./test/testdata/TTypefamily.hs" content <- liftIO $ Prelude.readFile filePath let expect = - [SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 4, startChar = 13, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 4, startChar = 17, len = 1}, name = "a"} - ,SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 5, startChar = 3, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 7, len = 3}, name = "Int"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Int"} - ,SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 6, startChar = 3, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 7, len = 1}, name = "a"} - ,SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 6, startChar = 11, len = 6}, name = "String"}] - runSessionWithServerInDirAndGetSemantic "TTypefamily.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" + [ SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 4, startChar = 13, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 4, startChar = 17, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 5, startChar = 3, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 7, len = 3}, name = "Int"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Int"}, + SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 6, startChar = 3, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 7, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 6, startChar = 11, len = 6}, name = "String"} + ] + let e2 = + [ SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 4, startChar = 13, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 5, startChar = 3, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 7, len = 3}, name = "Int"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Int"}, + SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 6, startChar = 3, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 6, startChar = 11, len = 6}, name = "String"} + ] - ,testCase "module import test" $ do + runSessionWithServerInDirAndGetSemantic "TTypefamily.hs" $ \res doc -> do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ length xs @?= length expect) + $ recoverSemanticTokens content tokens + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found", + testCase "module import test" $ do let filePath1 = "./test/testdata/TModuleA.hs" let filePath2 = "./test/testdata/TModuleB.hs" content1 <- liftIO $ Prelude.readFile filePath1 @@ -298,139 +338,234 @@ semanticTokensTests = let file1 = "TModuleA.hs" let file2 = "TModuleB.hs" - let expect = [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 2}, name = "go"} - ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 5, startChar = 6, len = 4}, name = "Game"}] + let expect = + [ SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 1, len = 2}, name = "go"}, + SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 5, startChar = 6, len = 4}, name = "Game"} + ] Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProjectMulti [file1, file2]) $ do - doc1 <- openDoc file1 "haskell" - doc2 <- openDoc file2 "haskell" - check1 <- waitForAction "TypeCheck" doc1 - check2 <- waitForAction "TypeCheck" doc2 - case check2 of - Right (WaitForIdeRuleResult x) -> return () - Left y -> error "TypeCheck2 failed" - - res2 <- Test.getSemanticTokens doc2 - case res2 ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content2 tokens - return () - _ -> error "No tokens found" - liftIO $ 1 @?= 1 - ] - + doc1 <- openDoc file1 "haskell" + doc2 <- openDoc file2 "haskell" + check1 <- waitForAction "TypeCheck" doc1 + check2 <- waitForAction "TypeCheck" doc2 + case check2 of + Right (WaitForIdeRuleResult x) -> return () + Left y -> error "TypeCheck2 failed" + + res2 <- Test.getSemanticTokens doc2 + case res2 ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content2 tokens + return () + _ -> error "No tokens found" + liftIO $ 1 @?= 1 + ] semanticTokensDataTypeTests = - testGroup + testGroup "get semantic Tokens" - [ testCase "datatype" $ do + [ testCase "simple datatype" $ do + + let filePath = "./test/testdata/TDataType.hs" content <- liftIO $ Prelude.readFile filePath let expect = - [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 3, startChar = 12, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 16, len = 3}, name = "Int"} - ,SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 3, startChar = 30, len = 2}, name = "Eq"}] + [ SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 3, startChar = 12, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 16, len = 3}, name = "Int"}, + SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 3, startChar = 30, len = 2}, name = "Eq"} + ] runSessionWithServerInDirAndGetSemantic "TDataType.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" - - -- record is part of datatype - , testCase "record" $ do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found", + -- record is part of datatype + testCase "record" $ do let filePath = "./test/testdata/TRecord.hs" content <- liftIO $ Prelude.readFile filePath let expect = - [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} - ,SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 4, startChar = 18, len = 3}, name = "foo"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 25, len = 3}, name = "Int"}] + [ SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"}, + SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 4, startChar = 18, len = 3}, name = "foo"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 25, len = 3}, name = "Int"} + ] runSessionWithServerInDirAndGetSemantic "TRecord.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" - , testCase "datatype import" $ do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found", + testCase "datatype import" $ do let filePath = "./test/testdata/TDatatypeImported.hs" content <- liftIO $ Prelude.readFile filePath let expect = - [SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 5, startChar = 1, len = 2}, name = "go"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 7, len = 2}, name = "IO"} - ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 6, startChar = 1, len = 2}, name = "go"} - ,SemanticTokenOriginal {tokenType = TValBind, loc = Loc {line = 6, startChar = 6, len = 5}, name = "print"}] + [ SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 1, len = 2}, name = "go"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 7, len = 2}, name = "IO"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 1, len = 2}, name = "go"}, + SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 6, len = 5}, name = "print"} + ] + runSessionWithServerInDirAndGetSemantic "TDatatypeImported.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" - , testCase "datatype family" $ do + -- content <- waitForAction "getFileContents" doc + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ length xs @?= length expect) + $ recoverSemanticTokens content tokens + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found", + testCase "datatype family" $ do let filePath = "./test/testdata/TDataFamily.hs" content <- liftIO $ Prelude.readFile filePath -- for data family, -- the data family name is type family -- its data constructor is also TDataCon let expect = - [SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 5, startChar = 13, len = 5} , name = "XList"} - ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 19, len = 1}, name = "a"} - ,SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 8, startChar = 15, len = 5}, name = "XList"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 21, len = 4}, name = "Char"} - ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 8, startChar = 28, len = 5}, name = "XCons"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 35, len = 4}, name = "Char"} - ,SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 8, startChar = 42, len = 5}, name = "XList"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 48, len = 4}, name = "Char"} - ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 8, startChar = 56, len = 4}, name = "XNil"} - ,SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 11, startChar = 15, len = 5}, name = "XList"} - ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 11, startChar = 26, len = 9}, name = "XListUnit"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 11, startChar = 37, len = 3}, name = "Int"}] + [ SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 5, startChar = 13, len = 5}, name = "XList"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 19, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 8, startChar = 15, len = 5}, name = "XList"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 21, len = 4}, name = "Char"}, + SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 8, startChar = 28, len = 5}, name = "XCons"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 35, len = 4}, name = "Char"}, + SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 8, startChar = 42, len = 5}, name = "XList"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 48, len = 4}, name = "Char"}, + SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 8, startChar = 56, len = 4}, name = "XNil"}, + SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 11, startChar = 15, len = 5}, name = "XList"}, + SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 11, startChar = 26, len = 9}, name = "XListUnit"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 11, startChar = 37, len = 3}, name = "Int"} + ] runSessionWithServerInDirAndGetSemantic "TDataFamily.hs" $ \res doc -> do - case res ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" - , testCase "GADT" $ do + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found", + testCase "GADT" $ do let filePath = "./test/testdata/TGADT.hs" content <- liftIO $ Prelude.readFile filePath -- for data family, -- the data family name is type family -- its data constructor is also TDataCon let expect = - [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 6, len = 3}, name = "Lam"} - ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 6, startChar = 3, len = 4}, name = "Lift"} - ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 11, len = 1}, name = "a"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 36, len = 3}, name = "Lam"} - ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 40, len = 1}, name = "a"} - ,SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 7, startChar = 3, len = 3}, name = "Lam"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 12, len = 3}, name = "Lam"} - ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 16, len = 1}, name = "a"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 21, len = 3}, name = "Lam"} - ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 25, len = 1}, name = "b"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 36, len = 3}, name = "Lam"} - ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 41, len = 1}, name = "a"} - ,SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 46, len = 1}, name = "b"}] + [ SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 6, len = 3}, name = "Lam"}, + SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 6, startChar = 3, len = 4}, name = "Lift"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 11, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 36, len = 3}, name = "Lam"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 40, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 7, startChar = 3, len = 3}, name = "Lam"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 12, len = 3}, name = "Lam"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 16, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 21, len = 3}, name = "Lam"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 25, len = 1}, name = "b"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 36, len = 3}, name = "Lam"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 41, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 46, len = 1}, name = "b"} + ] runSessionWithServerInDirAndGetSemantic "TGADT.hs" $ \res doc -> do - case res ^? _L of - Just tokens -> do - either (error . show) - (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found" ] - +semanticTokensFunctionTests = + testGroup + "get semantic of functions" + [ testCase "functions" $ do + let expect = + [ SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 3, startChar = 1, len = 1}, name = "f"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 3, startChar = 13, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 3, startChar = 16, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 3, startChar = 21, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "f"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 3, len = 1}, name = "x"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 7, len = 1}, name = "x"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 1, len = 1}, name = "x"}, + SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 6, len = 1}, name = "a"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 7, startChar = 1, len = 1}, name = "x"}, + SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 7, startChar = 5, len = 9}, name = "undefined"} + ] + runSessionWithServerInDirAndGetSemanticsFile "TFunction.hs" $ \res doc content -> do + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found", + testCase "local functions" $ do + let expect = + [SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 3, startChar = 1, len = 1}, name = "f"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Int"} + ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 13, len = 3}, name = "Int"} + ,SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "f"} + ,SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 7, len = 1}, name = "g"} + ,SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 5, len = 1}, name = "g"} + ,SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 7, len = 1}, name = "x"}] + + + runSessionWithServerInDirAndGetSemanticsFile "TFunctionLocal.hs" $ \res doc content -> do + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found", + testCase "function in let binding" $ do + let expect = + [ SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 3, startChar = 1, len = 1}, name = "y"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Int"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 1, len = 1}, name = "y"}, + SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 9, len = 1}, name = "f"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 11, len = 1}, name = "x"} + ] + + runSessionWithServerInDirAndGetSemanticsFile "TFunctionLet.hs" $ \res doc content -> do + case res ^? _L of + Just tokens -> do + either + (error . show) + (\xs -> liftIO $ xs @?= expect) + $ recoverSemanticTokens content tokens + return () + _notokens -> error "No tokens found" + ] main :: IO () -main = defaultTestRunner $ - testGroup "Semantic tokens" - [semanticTokensClassTests, semanticTokensDataTypeTests, semanticTokensValuePatternTests, semanticTokensTests] +main = + defaultTestRunner $ + testGroup + "Semantic tokens" + [ semanticTokensClassTests, + semanticTokensDataTypeTests, + semanticTokensValuePatternTests, + semanticTokensTests, + semanticTokensFunctionTests + ] diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.hs new file mode 100644 index 0000000000..4efe5cecc4 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.hs @@ -0,0 +1,7 @@ +module TFunction where + +f :: forall a. a -> a +f x = x + +x :: a +x = undefined diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.hs new file mode 100644 index 0000000000..96854c34ad --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.hs @@ -0,0 +1,4 @@ +module TFunctionLet where + +y :: Int +y = let f x = 1 in 1 diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.hs new file mode 100644 index 0000000000..fed144b00c --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.hs @@ -0,0 +1,8 @@ +module TFunctionLocal where + +f :: Int -> Int +f 1 = g 1 + where + g x = 1 + + diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.hs index 358d0a606a..6cfe5348a7 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.hs @@ -1,4 +1,4 @@ -module TPatternBind where +module TVariable where (a, b) = (1, 2) From 5ed3f38019c2c5fdd7b00005b26ee721a53b71c6 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 22 Dec 2023 02:23:44 +0800 Subject: [PATCH 35/73] cleanup --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 24 +++--- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 27 ++++--- .../src/Ide/Plugin/SemanticTokens/Query.hs | 79 ++++++++----------- .../hls-semantic-tokens-plugin/test/Main.hs | 26 +++--- 4 files changed, 73 insertions(+), 83 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 4a61dd1547..d26c2da412 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -104,8 +104,8 @@ logWith st prior = liftIO . logPriority (ideLogger st) prior. T.pack -- | computeSemanticTokens -- collect from various source --- imported name from GetGlobalNameSemantic --- local names from hieAst +-- imported name token type from GetGlobalNameSemantic +-- local names tokenType from hieAst -- name locations from hieAst -- visible names from renamedSource computeSemanticTokens :: IdeState -> NormalizedFilePath -> Action (Maybe SemanticTokens) @@ -120,9 +120,8 @@ computeSemanticTokens state nfp = -- because the names get from ast might contain derived name let nameSet = nameGetter tmrRenamed let spanNames = hieAstSpanNames nameSet ast - let localNameSemanticMapFromIdDetails = toNameSemanticMap nameSet refMap - let localNameSemanticMapFromTypes = fmap typeSemantic $ mkNameEnv $ getNameTypes ast refMap - let combineMap = plusNameEnv_C (<>) localNameSemanticMapFromTypes $ plusNameEnv_C (<>) localNameSemanticMapFromIdDetails getNameSemanticMap + let localNameSemanticMapFromTypes = getLocalNameSemantic ast refMap + let combineMap = plusNameEnv_C (<>) localNameSemanticMapFromTypes getNameSemanticMap let moduleAbsTks = extractSemanticTokensFromNames combineMap spanNames case semanticTokenAbsoluteSemanticTokens moduleAbsTks of Right tokens -> do @@ -136,17 +135,13 @@ semanticTokensFull :: PluginMethodHandler IdeState 'Method_TextDocumentSemanticT semanticTokensFull state _ param = do -- let dbg = logWith state nfp <- getNormalizedFilePathE (param ^. L.textDocument . L.uri) - -- dbg $ "semanticTokensFull: " <> show nfp - -- source :: ByteString <- lift $ getSourceFileSource nfp + logWith state Info $ "computeSemanticTokens: " <> show nfp items <- liftIO $ runAction "SemanticTokens.semanticTokensFull" state $ computeSemanticTokens state nfp case items of - Nothing -> pure $ InR Null - Just items -> do - -- content <- liftIO $ readFile $ fromNormalizedFilePath nfp - -- mapM_ (mapM_ (dbg . show)) $ recoverSemanticTokens content items - pure $ InL items + Nothing -> pure $ InR Null + Just items -> pure $ InL items getImportedNameSemanticRule :: Recorder (WithPriority Log) -> Rules () @@ -156,13 +151,14 @@ getImportedNameSemanticRule recorder = (hscEnv -> hsc, _) <- useWithStale_ GhcSessionDeps nfp let nameSet = nameGetter tmrRenamed tm <- liftIO $ foldrM (getTypeExclude (tcg_type_env tmrTypechecked) hsc) emptyNameEnv $ nameSetElemsStable nameSet - return ([],Just (GTTMap $ fmap tyThingSemantic $ plusNameEnv (tcg_type_env tmrTypechecked) tm)) + return ([],Just (GTTMap $ -- plusNameEnv (tcg_type_env tmrTypechecked) + tm)) where -- ignore one already in current module getTypeExclude localMap env n nameMap | Nothing <- lookupNameEnv localMap n = do tyThing <- lookupImported env n - pure $ maybe nameMap (extendNameEnv nameMap n) tyThing + pure $ maybe nameMap (extendNameEnv nameMap n . tyThingSemantic) tyThing | otherwise = pure nameMap lookupImported :: HscEnv -> Name -> IO (Maybe TyThing) lookupImported env = fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 2dd31a8736..3ab3333f6e 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -66,20 +66,21 @@ fromLspTokenType tk = fromMaybe TNothing $ Map.lookup tk lspTokenReverseMap -} typeSemantic :: Type -> SemanticTokenType -typeSemantic x = +typeSemantic x = case x of - ForAllTy _ a -> typeSemantic a - FunTy _ _ -> TFunction - TyVarTy _ -> TNothing - TyConApp tyCon _ -> TNothing - -- | isTypeSynonymTyCon tyCon -> TTypeSyn - -- | isTypeFamilyTyCon tyCon -> TTypeFamily - -- | isClassTyCon tyCon -> TClass - -- | otherwise -> TTypeCon - AppTy a b -> TNothing - LitTy _ -> TNothing - CastTy _ _ -> TNothing - CoercionTy _ -> TNothing + ForAllTy _ a -> typeSemantic a + FunTy _ _ -> TFunction + _ -> TVariable + -- TyVarTy _ -> TNothing + -- TyConApp tyCon _ -> TNothing + -- -- | isTypeSynonymTyCon tyCon -> TTypeSyn + -- -- | isTypeFamilyTyCon tyCon -> TTypeFamily + -- -- | isClassTyCon tyCon -> TClass + -- -- | otherwise -> TTypeCon + -- AppTy a b -> TNothing + -- LitTy _ -> TNothing + -- CastTy _ _ -> TNothing + -- CoercionTy _ -> TNothing toTokenType :: Name -> SemanticTokenType diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 76062e94ed..987088dfb7 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -70,44 +70,49 @@ nameGetter :: RenamedSource -> NameSet nameGetter = everything unionNameSet (emptyNameSet `mkQ` nameToCollect) -------------------------------------------------- ---- | extract location and type info from HieAST a +--- | extract semantic map from HieAst for local variables -------------------------------------------------- --- todo this is O(n^2) but we might do way better, -getNameTypes :: HieAST Type -> RefMap a -> [(Name, Type)] -getNameTypes ast rm = mapMaybe (nameType ast rm) ns +getLocalNameSemantic :: HieAST Type -> RefMap a -> NameSemanticMap +getLocalNameSemantic ast rm = mkNameEnv (mapMaybe (nameNameSemanticFromHie ast rm) ns) where ns = rights $ M.keys rm - -nameType :: HieAST Type -> RefMap a -> Name -> Maybe (Name,Type) -nameType hie rm ns = do - span <- -- traceShow ("to find Name", showName ns) $ - getDefinitionSite rm ns - ty <- -- traceShow ("nameBindSite", fmap typeSemantic $ spanTypeFromHie hie span, showName ns, span) $ - spanTypeFromHie hie span - return (ns, ty) +nameNameSemanticFromHie :: HieAST Type -> RefMap a -> Name -> Maybe (Name,SemanticTokenType) +nameNameSemanticFromHie hie rm ns = do + st <- -- traceShow ("to find Name", showName ns) $ + nameSemanticFromRefMap rm ns + return -- $ traceShow (showName ns, st) + (ns, st) where - getDefinitionSite :: RefMap a -> Name -> Maybe Span - getDefinitionSite rm name = do + nameSemanticFromRefMap :: RefMap a -> Name -> Maybe SemanticTokenType + nameSemanticFromRefMap rm name = do let nameString = showName name - xs <- -- traceShow ("getting spans:", nameString) $ - Map.lookup (Right name) rm - x <- -- traceShow ("getDefinitionSite:size:", nameString, length xs) $ - listToMaybe xs - let infos = concatMap (S.toList . identInfo. snd) xs - -- traceShow ("getDefinitionSite:infos", nameString, infos, mapMaybe getBindSiteFromContext infos) listToMaybe $ mapMaybe getBindSiteFromContext infos - listToMaybe $ mapMaybe getBindSiteFromContext infos + spanInfos <- -- traceShow ("getting spans:", nameString) $ + Map.lookup (Right name) rm + let infos = S.unions $ map (identInfo . snd) spanInfos + let bindTokenType = -- traceShow ("getDefinitionSite:infos", nameString, infos, mapMaybe getBindSiteFromContext $ S.toList infos) $ + listToMaybe (mapMaybe getBindSiteFromContext $ S.toList infos) >>= bindSiteMaybeTokenType + let contextInfoTokenType = contextInfosMaybeTokenType infos + maximum <$> NE.nonEmpty (catMaybes [bindTokenType, contextInfoTokenType]) + spanTypeFromHie :: HieAST Type -> Span -> Maybe Type spanTypeFromHie ast span = do ast <- selectSmallestContaining span ast - getNodeIType ast - getNodeIType hie = do - nodeInfo <- Map.lookup SourceInfo $ getSourcedNodeInfo $ sourcedNodeInfo hie - nt <- safeHead $ nodeType nodeInfo + nodeInfo <- Map.lookup SourceInfo $ getSourcedNodeInfo $ sourcedNodeInfo ast + -- usually for a visible name, the type is in the first child + nt <- listToMaybe $ nodeType nodeInfo return nt - safeHead :: [a] -> Maybe a - safeHead [] = Nothing - safeHead (x:_) = Just x + bindSiteMaybeTokenType :: Span -> Maybe SemanticTokenType + bindSiteMaybeTokenType = fmap typeSemantic . spanTypeFromHie hie + + contextInfosMaybeTokenType :: Set.Set ContextInfo -> Maybe SemanticTokenType + contextInfosMaybeTokenType details = case NE.nonEmpty $ Set.toList details of + Just infos -> Just $ maximum $ NE.map infoTokenType infos + Nothing -> Nothing + +-------------------------------------------------- +--- | extract location from HieAST a +-------------------------------------------------- hieAstSpanNames :: NameSet -> HieAST a -> [(Span, Name)] hieAstSpanNames nameSet ast = if null (nodeChildren ast) then @@ -139,24 +144,6 @@ hieAstSpanNames nameSet ast = if null (nodeChildren ast) then --- | extract semantic tokens from NameSemanticMap -------------------------------------------------- - -toNameSemanticMap :: NameSet -> RefMap a -> NameSemanticMap -toNameSemanticMap ns rm = extendNameEnvList_C (<>) emptyNameEnv - [ - -- trace ("toNameSemanticMap" <> ":" <> showSDocUnsafe (ppr name) <> " : " <> showCompactRealSrc span <> ":" <> showIdentifierDetails detail <> " : " <> show tokenType) - (name, tokenType) - | (name, Just details) <- map (\x -> (x, Map.lookup (Right x) rm)) $ nameSetElemsStable ns - , not $ isDerivedOccName (occName name) - , (span, detail) <- details - , let tokenType = detailSemanticMaybeTokenType $ identInfo detail - , (Just tokenType) <- [tokenType] - ] - where - detailSemanticMaybeTokenType :: Set.Set ContextInfo -> Maybe SemanticTokenType - detailSemanticMaybeTokenType details = case NE.nonEmpty $ Set.toList details of - Just infos -> Just $ maximum $ NE.map infoTokenType infos - Nothing -> Nothing - semanticTokenAbsoluteSemanticTokens :: [SemanticTokenAbsolute] -> Either Text SemanticTokens semanticTokenAbsoluteSemanticTokens = makeSemanticTokens defaultSemanticTokensLegend . List.sort diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 81b0a71014..8db17b8181 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -367,8 +367,6 @@ semanticTokensDataTypeTests = testGroup "get semantic Tokens" [ testCase "simple datatype" $ do - - let filePath = "./test/testdata/TDataType.hs" content <- liftIO $ Prelude.readFile filePath let expect = @@ -520,14 +518,22 @@ semanticTokensFunctionTests = _notokens -> error "No tokens found", testCase "local functions" $ do let expect = - [SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 3, startChar = 1, len = 1}, name = "f"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Int"} - ,SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 13, len = 3}, name = "Int"} - ,SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "f"} - ,SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 7, len = 1}, name = "g"} - ,SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 5, len = 1}, name = "g"} - ,SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 7, len = 1}, name = "x"}] - + [ SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 3, startChar = 1, len = 1}, name = "f"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Int"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 13, len = 3}, name = "Int"}, + SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "f"}, + SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 7, len = 1}, name = "g"}, + SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 5, len = 1}, name = "g"}, + SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 7, len = 1}, name = "x"} + ] + let e1 = + [ SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 3, startChar = 1, len = 1}, name = "f"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Int"}, + SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 13, len = 3}, name = "Int"}, + SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "f"}, + SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 7, len = 1}, name = "g"}, + SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 5, len = 1}, name = "g"} + ] runSessionWithServerInDirAndGetSemanticsFile "TFunctionLocal.hs" $ \res doc content -> do case res ^? _L of From e5c1e90488872ce173d1f06eb52eac56d6b11f82 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 22 Dec 2023 04:13:34 +0800 Subject: [PATCH 36/73] Refactor useWithStaleMT and took care of the token range using position map --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 52 ++++++++++++++----- .../src/Ide/Plugin/SemanticTokens/Query.hs | 31 +++++------ .../hls-semantic-tokens-plugin/test/Main.hs | 2 +- 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index d26c2da412..bc0a0cd624 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -14,7 +14,7 @@ module Ide.Plugin.SemanticTokens.Internal where import Control.Lens ((^.)) import Control.Monad.IO.Class (MonadIO, liftIO) -import Control.Monad.Trans.Maybe (MaybeT (..)) +import Control.Monad.Trans.Maybe (MaybeT (..), hoistMaybe) import Data.Data (Data) import Data.Generics (Generic, Typeable, everything, mkQ) @@ -75,9 +75,11 @@ import Data.Text (Text) import Data.Traversable (for) import Data.Typeable (cast) import Debug.Trace (trace) -import Development.IDE.Core.PluginUtils (useWithStaleFastMT, +import Development.IDE.Core.PluginUtils (toCurrentPositionMT, + useWithStaleFastMT, useWithStaleMT, usesMT) -import Development.IDE.Core.PositionMapping (zeroMapping) +import Development.IDE.Core.PositionMapping (toCurrentPosition, + zeroMapping) import Development.IDE.Spans.Documentation (mkDocMap) import Development.IDE.Spans.LocalBindings (getDefiningBindings, getLocalScope) @@ -90,6 +92,8 @@ import qualified Data.Set as S import Development.IDE (Rules, define, useWithStale_) import Development.IDE.Core.PluginUtils (useMT) +import Development.IDE.Core.PositionMapping (fromCurrentRange, + toCurrentRange) import Development.IDE.Core.Rules (Log) import Development.IDE.Graph.Classes (Hashable, NFData (rnf)) import Ide.Plugin.SemanticTokens.Mappings @@ -112,17 +116,16 @@ computeSemanticTokens :: IdeState -> NormalizedFilePath -> Action (Maybe Semanti computeSemanticTokens state nfp = runMaybeT $ do let logError = logWith state Debug - (HAR{hieAst, refMap}) <- useMT GetHieAst nfp - typedAst :: HieASTs Type <- MaybeT $ pure $ cast hieAst + (HAR{hieAst, refMap}, mapping) <- useWithStaleMT GetHieAst nfp (TcModuleResult{..}, _) <- useWithStaleMT TypeCheck nfp - (_, ast) <- MaybeT $ return $ listToMaybe $ Map.toList $ getAsts typedAst (GTTMap{getNameSemanticMap}, _) <- useWithStaleMT GetGlobalNameSemantic nfp - -- because the names get from ast might contain derived name + (_, ast) <- MaybeT $ return $ listToMaybe $ Map.toList $ getAsts hieAst let nameSet = nameGetter tmrRenamed - let spanNames = hieAstSpanNames nameSet ast - let localNameSemanticMapFromTypes = getLocalNameSemantic ast refMap - let combineMap = plusNameEnv_C (<>) localNameSemanticMapFromTypes getNameSemanticMap - let moduleAbsTks = extractSemanticTokensFromNames combineMap spanNames + -- get current location from the old ones + let spanNames = mapMaybe (\(span, name) -> (,name) <$> toCurrentRange mapping span) $ hieAstSpanNames nameSet ast + -- because the names get from ast might contain derived name + sMap <- mkNameSemanticMap hieAst refMap getNameSemanticMap + let moduleAbsTks = extractSemanticTokensFromNames sMap spanNames case semanticTokenAbsoluteSemanticTokens moduleAbsTks of Right tokens -> do source :: ByteString <- lift $ getSourceFileSource nfp @@ -149,10 +152,17 @@ getImportedNameSemanticRule recorder = define (cmapWithPrio LogShake recorder) $ \GetGlobalNameSemantic nfp -> do (TcModuleResult{..}, _) <- useWithStale_ TypeCheck nfp (hscEnv -> hsc, _) <- useWithStale_ GhcSessionDeps nfp + (HAR{hieAst, refMap}, _) <- useWithStale_ GetHieAst nfp + let x = mkLocalNameSemanticMap hieAst refMap let nameSet = nameGetter tmrRenamed + gMap <- liftIO $ mkImportedNameSemanticMap hsc tmrTypechecked nameSet + -- sMap <- fmap (mkNameSemanticMap hieAst refMap hsc) gMap + return ([], GTTMap <$> gMap) + +mkImportedNameSemanticMap :: MonadIO m => HscEnv -> TcGblEnv -> NameSet -> m (Maybe NameSemanticMap) +mkImportedNameSemanticMap hsc tmrTypechecked nameSet = do tm <- liftIO $ foldrM (getTypeExclude (tcg_type_env tmrTypechecked) hsc) emptyNameEnv $ nameSetElemsStable nameSet - return ([],Just (GTTMap $ -- plusNameEnv (tcg_type_env tmrTypechecked) - tm)) + return $ Just tm where -- ignore one already in current module getTypeExclude localMap env n nameMap @@ -163,3 +173,19 @@ getImportedNameSemanticRule recorder = lookupImported :: HscEnv -> Name -> IO (Maybe TyThing) lookupImported env = fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env + +-- mkNameSemanticMap :: HieASTs a -> RefMap a -> HscEnv -> TcGblEnv -> NameSet -> Maybe NameSemanticMap +-- mkNameSemanticMap :: (MonadIO m, Typeable a1) => HieASTs a1 -> RefMap a1 -> HscEnv -> TcGblEnv -> NameSet -> m (Maybe NameSemanticMap) +mkNameSemanticMap :: (Typeable a, Monad m) => HieASTs a -> RefMap a -> NameEnv SemanticTokenType -> MaybeT m (NameEnv SemanticTokenType) +mkNameSemanticMap hieAst rm gMap = do + let lMap = mkLocalNameSemanticMap hieAst rm + localNameSemanticMapFromTypes <- hoistMaybe lMap + let combineMap = plusNameEnv_C (<>) localNameSemanticMapFromTypes gMap + return combineMap + +mkLocalNameSemanticMap :: (Typeable a) => HieASTs a -> RefMap a -> Maybe NameSemanticMap +mkLocalNameSemanticMap hieAst rm = do + typedAst :: HieASTs Type <- cast hieAst + (_, ast) <- listToMaybe $ Map.toList $ getAsts typedAst + return $ mkLocalNameSemanticFromAst ast rm + diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 987088dfb7..6c20cd30a2 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -32,7 +32,8 @@ import Data.Tuple (swap) import Debug.Trace import Development.IDE (Action, filePathToUri', rangeToRealSrcSpan, - realSpan) + realSpan, + realSrcSpanToRange) import Development.IDE.GHC.Compat import Development.IDE.GHC.Error (positionToRealSrcLoc) import Development.IDE.Spans.AtPoint (FOIReferences) @@ -73,8 +74,9 @@ nameGetter = everything unionNameSet (emptyNameSet `mkQ` nameToCollect) --- | extract semantic map from HieAst for local variables -------------------------------------------------- -getLocalNameSemantic :: HieAST Type -> RefMap a -> NameSemanticMap -getLocalNameSemantic ast rm = mkNameEnv (mapMaybe (nameNameSemanticFromHie ast rm) ns) + +mkLocalNameSemanticFromAst :: HieAST Type -> RefMap a -> NameSemanticMap +mkLocalNameSemanticFromAst ast rm = mkNameEnv (mapMaybe (nameNameSemanticFromHie ast rm) ns) where ns = rights $ M.keys rm nameNameSemanticFromHie :: HieAST Type -> RefMap a -> Name -> Maybe (Name,SemanticTokenType) nameNameSemanticFromHie hie rm ns = do @@ -114,12 +116,12 @@ nameNameSemanticFromHie hie rm ns = do --- | extract location from HieAST a -------------------------------------------------- -hieAstSpanNames :: NameSet -> HieAST a -> [(Span, Name)] +hieAstSpanNames :: UniqSet Name -> HieAST a -> [(Range, Name)] hieAstSpanNames nameSet ast = if null (nodeChildren ast) then getIds ast else concatMap (hieAstSpanNames nameSet) (nodeChildren ast) where - getIds :: HieAST a -> [(Span, Name)] - getIds ast = [(nodeSpan ast, c) + -- getIds :: HieAST a -> [(Span, Name)] + getIds ast = [(realSrcSpanToRange $ nodeSpan ast, c) | (Right c, d) <- Map.toList $ getNodeIds' ast , elemNameSet c nameSet -- at least get one info @@ -147,24 +149,23 @@ hieAstSpanNames nameSet ast = if null (nodeChildren ast) then semanticTokenAbsoluteSemanticTokens :: [SemanticTokenAbsolute] -> Either Text SemanticTokens semanticTokenAbsoluteSemanticTokens = makeSemanticTokens defaultSemanticTokensLegend . List.sort -extractSemanticTokensFromNames :: NameSemanticMap -> [(Span, Name)] -> [SemanticTokenAbsolute] +-- extractSemanticTokensFromNames :: NameSemanticMap -> [(Span, Name)] -> [SemanticTokenAbsolute] +extractSemanticTokensFromNames :: UniqFM Name SemanticTokenType -> [(Range, Name)] -> [SemanticTokenAbsolute] extractSemanticTokensFromNames nsm = mapMaybe (uncurry toAbsSemanticToken) . mergeNameFromSamSpan . mapMaybe (getSemantic nsm) where -- merge all tokens with same span - mergeNameFromSamSpan :: [(Span, SemanticTokenType)] -> [(Span, SemanticTokenType)] + -- mergeNameFromSamSpan :: [(Span, SemanticTokenType)] -> [(Span, SemanticTokenType)] mergeNameFromSamSpan xs = Map.toList $ Map.fromListWith (<>) xs - toAbsSemanticToken :: Span -> SemanticTokenType -> Maybe SemanticTokenAbsolute - toAbsSemanticToken loc tokenType = - let line = srcSpanStartLine loc - 1 - startChar = srcSpanStartCol loc - 1 - len = srcSpanEndCol loc - 1 - startChar - in SemanticTokenAbsolute (fromIntegral line) (fromIntegral startChar) + toAbsSemanticToken :: Range -> SemanticTokenType -> Maybe SemanticTokenAbsolute + toAbsSemanticToken (Range (Position startLine startColumn) (Position endLine endColumn)) tokenType = + let len = endColumn - startColumn + in SemanticTokenAbsolute (fromIntegral startLine) (fromIntegral startColumn) (fromIntegral len) <$> toLspTokenType tokenType <*> return [] -- SemanticTokenModifiers_Declaration - getSemantic :: NameSemanticMap -> (Span, Name) -> Maybe (Span, SemanticTokenType) + -- getSemantic :: NameSemanticMap -> (Span, Name) -> Maybe (Span, SemanticTokenType) getSemantic nameMap (span, name) = do -- let tkt = toTokenType name -- let tokenType = maybe tkt (\x -> tkt <> x) $ Map.lookup name nameMap diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 8db17b8181..60a82cf30a 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -534,7 +534,7 @@ semanticTokensFunctionTests = SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 7, len = 1}, name = "g"}, SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 5, len = 1}, name = "g"} ] - + let f x = 1 runSessionWithServerInDirAndGetSemanticsFile "TFunctionLocal.hs" $ \res doc content -> do case res ^? _L of Just tokens -> do From 8ea6e1a3817eab8a5bac3b826e23bd0c41ddcdf1 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 22 Dec 2023 04:50:19 +0800 Subject: [PATCH 37/73] fix build for 9.4 --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 12 +++++------- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 2 +- .../src/Ide/Plugin/SemanticTokens/Types.hs | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index bc0a0cd624..43ef9444d8 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -14,7 +14,7 @@ module Ide.Plugin.SemanticTokens.Internal where import Control.Lens ((^.)) import Control.Monad.IO.Class (MonadIO, liftIO) -import Control.Monad.Trans.Maybe (MaybeT (..), hoistMaybe) +import Control.Monad.Trans.Maybe (MaybeT (..)) import Data.Data (Data) import Data.Generics (Generic, Typeable, everything, mkQ) @@ -118,13 +118,13 @@ computeSemanticTokens state nfp = let logError = logWith state Debug (HAR{hieAst, refMap}, mapping) <- useWithStaleMT GetHieAst nfp (TcModuleResult{..}, _) <- useWithStaleMT TypeCheck nfp - (GTTMap{getNameSemanticMap}, _) <- useWithStaleMT GetGlobalNameSemantic nfp + (GTTMap{importedNameSemanticMap}, _) <- useWithStaleMT GetGlobalNameSemantic nfp (_, ast) <- MaybeT $ return $ listToMaybe $ Map.toList $ getAsts hieAst let nameSet = nameGetter tmrRenamed -- get current location from the old ones let spanNames = mapMaybe (\(span, name) -> (,name) <$> toCurrentRange mapping span) $ hieAstSpanNames nameSet ast -- because the names get from ast might contain derived name - sMap <- mkNameSemanticMap hieAst refMap getNameSemanticMap + sMap <- MaybeT $ return $ mkNameSemanticMap hieAst refMap importedNameSemanticMap let moduleAbsTks = extractSemanticTokensFromNames sMap spanNames case semanticTokenAbsoluteSemanticTokens moduleAbsTks of Right tokens -> do @@ -153,7 +153,6 @@ getImportedNameSemanticRule recorder = (TcModuleResult{..}, _) <- useWithStale_ TypeCheck nfp (hscEnv -> hsc, _) <- useWithStale_ GhcSessionDeps nfp (HAR{hieAst, refMap}, _) <- useWithStale_ GetHieAst nfp - let x = mkLocalNameSemanticMap hieAst refMap let nameSet = nameGetter tmrRenamed gMap <- liftIO $ mkImportedNameSemanticMap hsc tmrTypechecked nameSet -- sMap <- fmap (mkNameSemanticMap hieAst refMap hsc) gMap @@ -176,10 +175,9 @@ mkImportedNameSemanticMap hsc tmrTypechecked nameSet = do -- mkNameSemanticMap :: HieASTs a -> RefMap a -> HscEnv -> TcGblEnv -> NameSet -> Maybe NameSemanticMap -- mkNameSemanticMap :: (MonadIO m, Typeable a1) => HieASTs a1 -> RefMap a1 -> HscEnv -> TcGblEnv -> NameSet -> m (Maybe NameSemanticMap) -mkNameSemanticMap :: (Typeable a, Monad m) => HieASTs a -> RefMap a -> NameEnv SemanticTokenType -> MaybeT m (NameEnv SemanticTokenType) +mkNameSemanticMap :: (Typeable a) => HieASTs a -> RefMap a -> NameEnv SemanticTokenType -> Maybe NameSemanticMap mkNameSemanticMap hieAst rm gMap = do - let lMap = mkLocalNameSemanticMap hieAst rm - localNameSemanticMapFromTypes <- hoistMaybe lMap + localNameSemanticMapFromTypes <- mkLocalNameSemanticMap hieAst rm let combineMap = plusNameEnv_C (<>) localNameSemanticMapFromTypes gMap return combineMap diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 3ab3333f6e..75ceed0459 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -70,7 +70,7 @@ typeSemantic x = case x of ForAllTy _ a -> typeSemantic a FunTy _ _ -> TFunction - _ -> TVariable + _ -> TNothing -- TyVarTy _ -> TNothing -- TyConApp tyCon _ -> TNothing -- -- | isTypeSynonymTyCon tyCon -> TTypeSyn diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 4014402a2b..9272f7cc68 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -69,7 +69,7 @@ data GetGlobalNameSemantic = GetGlobalNameSemantic instance Hashable GetGlobalNameSemantic instance NFData GetGlobalNameSemantic -data GlobalTokenTypeMap = GTTMap {getNameSemanticMap :: !NameSemanticMap } +data GlobalTokenTypeMap = GTTMap {importedNameSemanticMap :: !NameSemanticMap } instance NFData GlobalTokenTypeMap where rnf (GTTMap a) = rwhnf a instance Show GlobalTokenTypeMap where From b928270a119c218fffcb4263152ec1a9eb0b8062 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 22 Dec 2023 06:07:51 +0800 Subject: [PATCH 38/73] refactor, use golden test --- .../hls-semantic-tokens-plugin/test/Main.hs | 488 +++--------------- .../test/testdata/T1.expected | 81 +++ .../test/testdata/TClass.expected | 5 + .../testdata/TClassImportedDeriving.expected | 3 + .../test/testdata/TDataFamily.expected | 12 + .../test/testdata/TDataType.expected | 4 + .../test/testdata/TDatatypeImported.expected | 4 + .../test/testdata/TFunction.expected | 11 + .../test/testdata/TFunctionLet.expected | 5 + .../test/testdata/TFunctionLocal.expected | 7 + .../test/testdata/TGADT.expected | 13 + .../TInstanceClassMethodBind.expected | 7 + .../testdata/TInstanceClassMethodUse.expected | 2 + .../test/testdata/TPatternMatch.expected | 2 + .../test/testdata/TPatternSyn.expected | 1 + .../test/testdata/TPatternbind.expected | 8 + .../test/testdata/TRecord.expected | 4 + .../test/testdata/TTypefamily.expected | 8 + .../test/testdata/TValBind.expected | 4 + 19 files changed, 239 insertions(+), 430 deletions(-) create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TClass.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TDataFamily.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TDataType.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TPatternSyn.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.expected diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 60a82cf30a..c2e75137db 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -6,7 +6,7 @@ import Control.Arrow (Arrow ((***)), (&&&), (+++)) -import Control.Lens hiding (use) +import Control.Lens hiding (use, (<.>)) import Control.Monad (forM) import Data.Bifunctor import qualified Data.ByteString as BS @@ -14,12 +14,14 @@ import Data.Data import Data.Default import Data.Functor (void) import qualified Data.List as List -import Data.Map as Map +import Data.Map as Map hiding (map) import Data.Maybe (fromJust) import qualified Data.Maybe import qualified Data.Set as Set import Data.String (fromString) -import Data.Text hiding (length) +import Data.Text hiding (length, map, + unlines) +import Development.IDE (getFileContents, runAction) import Development.IDE.Core.Rules (Log) import Development.IDE.Plugin.Test (WaitForIdeRuleResult (..)) import Ide.Plugin.Error (getNormalizedFilePathE) @@ -76,261 +78,67 @@ runSessionWithServerInDirAndGetSemanticsFile file x = res <- Test.getSemanticTokens doc x res doc content +goldenWithHaskellAndCapsOutPut config plugin title tree path desc act = + goldenGitDiff title (FS.vftOriginalRoot tree path <.> desc) $ + runSessionWithServerInTmpDir config plugin tree $ + fromString <$> do + doc <- openDoc (path <.> "hs") "haskell" + void waitForBuildQueue + r <- act doc + return r + +goldenWithSemanticTokens :: TestName -> FilePath -> TestTree +goldenWithSemanticTokens title path = + goldenWithHaskellAndCapsOutPut + def + semanticTokensPlugin + title + (mkFs $ FS.directProject (path <.> "hs")) + path + "expected" + docSemanticTokensString + +docSemanticTokensString :: TextDocumentIdentifier -> Session String +docSemanticTokensString doc = do + res <- Test.getSemanticTokens doc + content <- unpack <$> documentContents doc + let expect = [] + case res ^? _L of + Just tokens -> do + either (error . show) (return . unlines . map show) $ recoverSemanticTokens content tokens + _noTokens -> error "No tokens found" + semanticTokensImportedTests :: TestTree semanticTokensImportedTests = testGroup "imported test" - [ testCase "type class" $ do - let expect = - [ SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 26, len = 4}, name = "Show"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 1, len = 2}, name = "eq"}, - SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 6, startChar = 8, len = 2}, name = "Eq"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 11, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 17, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 22, len = 4}, name = "Bool"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 7, startChar = 1, len = 2}, name = "eq"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 7, startChar = 4, len = 1}, name = "b"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 7, startChar = 8, len = 1}, name = "b"}, - SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 7, startChar = 10, len = 2}, name = "=="}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 7, startChar = 13, len = 1}, name = "b"} - ] - - runSessionWithServerInDirAndGetSemanticsFile "TClass.hs" $ \res doc content -> do - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" + [ goldenWithSemanticTokens "type class" "TClass" ] semanticTokensClassTests :: TestTree semanticTokensClassTests = testGroup "type class" - [ testCase "type class" $ do - let filePath = "./test/testdata/TClass.hs" - content <- liftIO $ Prelude.readFile filePath - let expect = - [ SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 7, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 4, startChar = 11, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 3, len = 3}, name = "foo"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 10, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 15, len = 3}, name = "Int"} - ] - runSessionWithServerInDirAndGetSemantic "TClass.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _notoken -> error "No tokens found", - testCase "imported class method InstanceClassMethodBind" $ do - let filePath = "./test/testdata/TInstanceClassMethodBind.hs" - content <- liftIO $ Prelude.readFile filePath - let expect = - [ SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 16, len = 3}, name = "Int"}, - SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 5, startChar = 10, len = 2}, name = "Eq"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 6, startChar = 5, len = 4}, name = "(==)"}, - SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 12, len = 9}, name = "undefined"} - ] - runSessionWithServerInDirAndGetSemantic "TInstanceClassMethodBind.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found", - testCase "imported class method TInstanceClassMethodUse" $ do - let filePath = "./test/testdata/TInstanceClassMethodUse.hs" - content <- liftIO $ Prelude.readFile filePath - let expect = - [ SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 2}, name = "go"}, - SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 4, startChar = 9, len = 4}, name = "(==)"} - ] - runSessionWithServerInDirAndGetSemantic "TInstanceClassMethodUse.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either (error . show) (\xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found", - testCase "imported deriving" $ do - let filePath = "./test/testdata/TClassImportedDeriving.hs" - content <- liftIO $ Prelude.readFile filePath - let expect = - [ SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 26, len = 4}, name = "Show"} - ] - runSessionWithServerInDirAndGetSemantic "TClassImportedDeriving.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" + [ goldenWithSemanticTokens "golden type class" "TClass", + goldenWithSemanticTokens "imported class method InstanceClassMethodBind" "TInstanceClassMethodBind", + goldenWithSemanticTokens "imported class method TInstanceClassMethodUse" "TInstanceClassMethodUse", + goldenWithSemanticTokens "imported deriving" "TClassImportedDeriving" ] semanticTokensValuePatternTests :: TestTree semanticTokensValuePatternTests = testGroup "value and patterns " - [ testCase "value bind" $ do - let filePath = "./test/testdata/TValBind.hs" - content <- liftIO $ Prelude.readFile filePath - let expect = - [ SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 1, len = 5}, name = "hello"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 10, len = 3}, name = "Int"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 1, len = 5}, name = "hello"}, - SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 9, len = 6}, name = "length"} - ] - runSessionWithServerInDirAndGetSemantic "TValBind.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _noTokens -> error "No tokens found", - testCase "pattern match" $ do - let filePath = "./test/testdata/TPatternMatch.hs" - content <- liftIO $ Prelude.readFile filePath - let expect = - [ SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "g"}, - SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 4, len = 7}, name = "Nothing"} - ] - runSessionWithServerInDirAndGetSemantic "TPatternMatch.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _noTokens -> error "No tokens found", - testCase "pattern bind" $ do - let filePath = "./test/testdata/TPatternbind.hs" - content <- liftIO $ Prelude.readFile filePath - let expect = - [ SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 3, startChar = 2, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 3, startChar = 5, len = 1}, name = "b"}, - SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 5, startChar = 1, len = 1}, name = "f"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 3, len = 1}, name = "x"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 5, len = 1}, name = "y"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 9, len = 1}, name = "x"}, - SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 11, len = 1}, name = "+"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 13, len = 1}, name = "y"} - ] - runSessionWithServerInDirAndGetSemantic "TPatternbind.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ length xs @?= length expect) - $ recoverSemanticTokens content tokens - either - (error . show) - ( \xs -> liftIO $ forM (Prelude.zip xs expect) $ \(x, y) -> do - x @?= y - return () - ) - $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" + [ goldenWithSemanticTokens "value bind" "TValBind", + goldenWithSemanticTokens "pattern match" "TPatternMatch", + goldenWithSemanticTokens "pattern bind" "TPatternbind" ] semanticTokensTests :: TestTree semanticTokensTests = testGroup "other semantic Token test" - [ -- testCase "mixed constancy test result generated from one ghc version" $ do - -- let filePath = "./test/testdata/T1.hs" - -- content <- liftIO $ Prelude.readFile filePath - -- let expect = [SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 18, len = 3}, name = "Set"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 23, len = 6}, name = "insert"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 6, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 9, startChar = 12, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 9, startChar = 18, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 25, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 11, startChar = 7, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 11, startChar = 11, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 12, startChar = 3, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 10, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 15, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 14, startChar = 10, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 14, startChar = 14, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 5, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 15, startChar = 9, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 15, startChar = 13, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 15, len = 1}, name = "+"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 6, len = 2}, name = "Dd"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 17, startChar = 11, len = 2}, name = "Dd"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 14, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 19, startChar = 9, len = 3}, name = "One"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 19, startChar = 15, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 21, startChar = 1, len = 3}, name = "ggg"},SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 21, startChar = 7, len = 3}, name = "One"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 6, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 23, startChar = 12, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 16, len = 11}, name = "Prelude.Int"},SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 24, startChar = 6, len = 4}, name = "Bar1"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 24, startChar = 13, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 25, startChar = 6, len = 4}, name = "Bar2"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 25, startChar = 13, len = 3}, name = "Doo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 27, startChar = 1, len = 2}, name = "bb"},SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 27, startChar = 8, len = 3}, name = "Boo"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 12, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 18, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 23, len = 1}, name = "a"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 28, startChar = 1, len = 2}, name = "bb"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 28, startChar = 4, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 28, startChar = 9, len = 3}, name = "boo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 28, startChar = 13, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 29, startChar = 1, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 7, len = 4}, name = "cool"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 29, startChar = 15, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 22, len = 4}, name = "cool"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 1, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 4, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 9, len = 1}, name = "c"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 14, len = 2}, name = "aa"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 17, len = 1}, name = "x"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 19, len = 1}, name = "c"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 31, startChar = 12, len = 2}, name = "xx"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 31, startChar = 16, len = 2}, name = "yy"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 32, startChar = 11, len = 2}, name = "dd"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 34, startChar = 2, len = 2}, name = "zz"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 34, startChar = 6, len = 2}, name = "kk"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 35, startChar = 1, len = 2}, name = "cc"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 7, len = 3}, name = "Foo"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 15, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 20, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 28, len = 3}, name = "Int"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 36, startChar = 1, len = 2}, name = "cc"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 36, startChar = 4, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 36, startChar = 7, len = 2}, name = "gg"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 36, startChar = 11, len = 2}, name = "vv"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 37, startChar = 10, len = 2}, name = "gg"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 14, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 38, startChar = 18, len = 1}, name = "$"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 38, startChar = 20, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 24, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 14, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 39, startChar = 18, len = 1}, name = "$"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 39, startChar = 20, len = 1}, name = "f"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 24, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 41, startChar = 1, len = 2}, name = "go"},SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 41, startChar = 6, len = 3}, name = "foo"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 42, startChar = 1, len = 3}, name = "add"},SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 42, startChar = 7, len = 11}, name = "(Prelude.+)"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 47, startChar = 1, len = 4}, name = "main"},SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 47, startChar = 9, len = 2}, name = "IO"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 48, startChar = 1, len = 4}, name = "main"},SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 48, startChar = 8, len = 8}, name = "putStrLn"}] - -- runSessionWithServerInDirAndGetSemantic "T1.hs" $ \res doc -> do - -- case res ^? _L of - -- Just tokens -> do - -- either (error . show) (\ xs -> liftIO $ xs @?= expect) $ recoverSemanticTokens content tokens - -- return () - -- _notokens -> error "No tokens found" - testCase "pattern syn" $ do - let filePath = "./test/testdata/TPatternSyn.hs" - content <- liftIO $ Prelude.readFile filePath - let expect = - [ SemanticTokenOriginal - { tokenType = TPatternSyn, - loc = Loc {line = 5, startChar = 9, len = 3}, - name = "Foo" - } - ] - runSessionWithServerInDirAndGetSemantic "TPatternSyn.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found", - testCase "type family" $ do - let filePath = "./test/testdata/TTypefamily.hs" - content <- liftIO $ Prelude.readFile filePath - let expect = - [ SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 4, startChar = 13, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 4, startChar = 17, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 5, startChar = 3, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 7, len = 3}, name = "Int"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Int"}, - SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 6, startChar = 3, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 7, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 6, startChar = 11, len = 6}, name = "String"} - ] - let e2 = - [ SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 4, startChar = 13, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 5, startChar = 3, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 7, len = 3}, name = "Int"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Int"}, - SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 6, startChar = 3, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 6, startChar = 11, len = 6}, name = "String"} - ] - - runSessionWithServerInDirAndGetSemantic "TTypefamily.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ length xs @?= length expect) - $ recoverSemanticTokens content tokens - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found", - testCase "module import test" $ do + [ testCase "module import test" $ do let filePath1 = "./test/testdata/TModuleA.hs" let filePath2 = "./test/testdata/TModuleB.hs" content1 <- liftIO $ Prelude.readFile filePath1 @@ -360,208 +168,28 @@ semanticTokensTests = $ recoverSemanticTokens content2 tokens return () _ -> error "No tokens found" - liftIO $ 1 @?= 1 + liftIO $ 1 @?= 1, + goldenWithSemanticTokens "mixed constancy test result generated from one ghc version" "T1", + goldenWithSemanticTokens "pattern bind" "TPatternSyn", + goldenWithSemanticTokens "type family" "TTypefamily" ] semanticTokensDataTypeTests = testGroup "get semantic Tokens" - [ testCase "simple datatype" $ do - let filePath = "./test/testdata/TDataType.hs" - content <- liftIO $ Prelude.readFile filePath - let expect = - [ SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 3, startChar = 12, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 16, len = 3}, name = "Int"}, - SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 3, startChar = 30, len = 2}, name = "Eq"} - ] - runSessionWithServerInDirAndGetSemantic "TDataType.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found", - -- record is part of datatype - testCase "record" $ do - let filePath = "./test/testdata/TRecord.hs" - content <- liftIO $ Prelude.readFile filePath - let expect = - [ SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"}, - SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 4, startChar = 18, len = 3}, name = "foo"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 25, len = 3}, name = "Int"} - ] - runSessionWithServerInDirAndGetSemantic "TRecord.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found", - testCase "datatype import" $ do - let filePath = "./test/testdata/TDatatypeImported.hs" - content <- liftIO $ Prelude.readFile filePath - let expect = - [ SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 1, len = 2}, name = "go"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 7, len = 2}, name = "IO"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 1, len = 2}, name = "go"}, - SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 6, len = 5}, name = "print"} - ] - - runSessionWithServerInDirAndGetSemantic "TDatatypeImported.hs" $ \res doc -> do - -- content <- waitForAction "getFileContents" doc - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ length xs @?= length expect) - $ recoverSemanticTokens content tokens - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found", - testCase "datatype family" $ do - let filePath = "./test/testdata/TDataFamily.hs" - content <- liftIO $ Prelude.readFile filePath - -- for data family, - -- the data family name is type family - -- its data constructor is also TDataCon - let expect = - [ SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 5, startChar = 13, len = 5}, name = "XList"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 19, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 8, startChar = 15, len = 5}, name = "XList"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 21, len = 4}, name = "Char"}, - SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 8, startChar = 28, len = 5}, name = "XCons"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 35, len = 4}, name = "Char"}, - SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 8, startChar = 42, len = 5}, name = "XList"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 48, len = 4}, name = "Char"}, - SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 8, startChar = 56, len = 4}, name = "XNil"}, - SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 11, startChar = 15, len = 5}, name = "XList"}, - SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 11, startChar = 26, len = 9}, name = "XListUnit"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 11, startChar = 37, len = 3}, name = "Int"} - ] - runSessionWithServerInDirAndGetSemantic "TDataFamily.hs" $ \res doc -> do - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found", - testCase "GADT" $ do - let filePath = "./test/testdata/TGADT.hs" - content <- liftIO $ Prelude.readFile filePath - -- for data family, - -- the data family name is type family - -- its data constructor is also TDataCon - let expect = - [ SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 6, len = 3}, name = "Lam"}, - SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 6, startChar = 3, len = 4}, name = "Lift"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 11, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 36, len = 3}, name = "Lam"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 40, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 7, startChar = 3, len = 3}, name = "Lam"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 12, len = 3}, name = "Lam"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 16, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 21, len = 3}, name = "Lam"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 25, len = 1}, name = "b"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 36, len = 3}, name = "Lam"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 41, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 46, len = 1}, name = "b"} - ] - runSessionWithServerInDirAndGetSemantic "TGADT.hs" $ \res doc -> do - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" + [ goldenWithSemanticTokens "simple datatype" "TDataType", + goldenWithSemanticTokens "record" "TRecord", + goldenWithSemanticTokens "datatype import" "TDatatypeImported", + goldenWithSemanticTokens "datatype family" "TDataFamily", + goldenWithSemanticTokens "GADT" "TGADT" ] semanticTokensFunctionTests = testGroup "get semantic of functions" - [ testCase "functions" $ do - let expect = - [ SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 3, startChar = 1, len = 1}, name = "f"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 3, startChar = 13, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 3, startChar = 16, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 3, startChar = 21, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "f"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 3, len = 1}, name = "x"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 7, len = 1}, name = "x"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 1, len = 1}, name = "x"}, - SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 6, len = 1}, name = "a"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 7, startChar = 1, len = 1}, name = "x"}, - SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 7, startChar = 5, len = 9}, name = "undefined"} - ] - runSessionWithServerInDirAndGetSemanticsFile "TFunction.hs" $ \res doc content -> do - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found", - testCase "local functions" $ do - let expect = - [ SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 3, startChar = 1, len = 1}, name = "f"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Int"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 13, len = 3}, name = "Int"}, - SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "f"}, - SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 7, len = 1}, name = "g"}, - SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 5, len = 1}, name = "g"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 7, len = 1}, name = "x"} - ] - let e1 = - [ SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 3, startChar = 1, len = 1}, name = "f"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Int"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 13, len = 3}, name = "Int"}, - SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "f"}, - SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 7, len = 1}, name = "g"}, - SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 5, len = 1}, name = "g"} - ] - let f x = 1 - runSessionWithServerInDirAndGetSemanticsFile "TFunctionLocal.hs" $ \res doc content -> do - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found", - testCase "function in let binding" $ do - let expect = - [ SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 3, startChar = 1, len = 1}, name = "y"}, - SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Int"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 1, len = 1}, name = "y"}, - SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 9, len = 1}, name = "f"}, - SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 11, len = 1}, name = "x"} - ] - - runSessionWithServerInDirAndGetSemanticsFile "TFunctionLet.hs" $ \res doc content -> do - case res ^? _L of - Just tokens -> do - either - (error . show) - (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content tokens - return () - _notokens -> error "No tokens found" + [ goldenWithSemanticTokens "functions" "TFunction", + goldenWithSemanticTokens "local functions" "TFunctionLocal", + goldenWithSemanticTokens "function in let binding" "TFunctionLet" ] main :: IO () @@ -569,9 +197,9 @@ main = defaultTestRunner $ testGroup "Semantic tokens" - [ semanticTokensClassTests, + [ semanticTokensTests, + semanticTokensClassTests, semanticTokensDataTypeTests, semanticTokensValuePatternTests, - semanticTokensTests, semanticTokensFunctionTests ] diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected new file mode 100644 index 0000000000..07f39f21c4 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected @@ -0,0 +1,81 @@ +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 18, len = 3}, name = "Set"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 23, len = 6}, name = "insert"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 6, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 9, startChar = 12, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 9, startChar = 18, len = 3}, name = "foo"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 25, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 11, startChar = 7, len = 3}, name = "Boo"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 11, startChar = 11, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 12, startChar = 3, len = 3}, name = "boo"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 10, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 15, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 14, startChar = 10, len = 3}, name = "Boo"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 14, startChar = 14, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 5, len = 3}, name = "boo"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 15, startChar = 9, len = 1}, name = "x"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 15, startChar = 13, len = 1}, name = "x"} +SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 15, len = 1}, name = "+"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 6, len = 2}, name = "Dd"} +SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 17, startChar = 11, len = 2}, name = "Dd"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 14, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 19, startChar = 9, len = 3}, name = "One"} +SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 19, startChar = 15, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 21, startChar = 1, len = 3}, name = "ggg"} +SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 21, startChar = 7, len = 3}, name = "One"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 6, len = 3}, name = "Doo"} +SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 23, startChar = 12, len = 3}, name = "Doo"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 16, len = 11}, name = "Prelude.Int"} +SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 24, startChar = 6, len = 4}, name = "Bar1"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 24, startChar = 13, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 25, startChar = 6, len = 4}, name = "Bar2"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 25, startChar = 13, len = 3}, name = "Doo"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 27, startChar = 1, len = 2}, name = "bb"} +SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 27, startChar = 8, len = 3}, name = "Boo"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 12, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 18, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 23, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 28, startChar = 1, len = 2}, name = "bb"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 28, startChar = 4, len = 1}, name = "x"} +SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 28, startChar = 9, len = 3}, name = "boo"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 28, startChar = 13, len = 1}, name = "x"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 29, startChar = 1, len = 2}, name = "aa"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 7, len = 4}, name = "cool"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 29, startChar = 15, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 22, len = 4}, name = "cool"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 30, startChar = 1, len = 2}, name = "aa"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 4, len = 1}, name = "x"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 9, len = 1}, name = "c"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 30, startChar = 14, len = 2}, name = "aa"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 17, len = 1}, name = "x"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 19, len = 1}, name = "c"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 31, startChar = 12, len = 2}, name = "xx"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 31, startChar = 16, len = 2}, name = "yy"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 32, startChar = 11, len = 2}, name = "dd"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 34, startChar = 2, len = 2}, name = "zz"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 34, startChar = 6, len = 2}, name = "kk"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 35, startChar = 1, len = 2}, name = "cc"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 7, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 15, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 20, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 28, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 36, startChar = 1, len = 2}, name = "cc"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 36, startChar = 4, len = 1}, name = "f"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 36, startChar = 7, len = 2}, name = "gg"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 36, startChar = 11, len = 2}, name = "vv"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 37, startChar = 10, len = 2}, name = "gg"} +SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 14, len = 3}, name = "foo"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 38, startChar = 18, len = 1}, name = "$"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 38, startChar = 20, len = 1}, name = "f"} +SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 24, len = 3}, name = "foo"} +SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 14, len = 3}, name = "foo"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 39, startChar = 18, len = 1}, name = "$"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 39, startChar = 20, len = 1}, name = "f"} +SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 24, len = 3}, name = "foo"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 41, startChar = 1, len = 2}, name = "go"} +SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 41, startChar = 6, len = 3}, name = "foo"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 42, startChar = 1, len = 3}, name = "add"} +SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 42, startChar = 7, len = 11}, name = "(Prelude.+)"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 47, startChar = 1, len = 4}, name = "main"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 47, startChar = 9, len = 2}, name = "IO"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 48, startChar = 1, len = 4}, name = "main"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 48, startChar = 8, len = 8}, name = "putStrLn"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TClass.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TClass.expected new file mode 100644 index 0000000000..e78882a1c1 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TClass.expected @@ -0,0 +1,5 @@ +SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 7, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 4, startChar = 11, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 3, len = 3}, name = "foo"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 10, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 15, len = 3}, name = "Int"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.expected new file mode 100644 index 0000000000..03002aa1b7 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.expected @@ -0,0 +1,3 @@ +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 26, len = 4}, name = "Show"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TDataFamily.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TDataFamily.expected new file mode 100644 index 0000000000..c66bef2ecd --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TDataFamily.expected @@ -0,0 +1,12 @@ +SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 5, startChar = 13, len = 5}, name = "XList"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 19, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 8, startChar = 15, len = 5}, name = "XList"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 21, len = 4}, name = "Char"} +SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 8, startChar = 28, len = 5}, name = "XCons"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 35, len = 4}, name = "Char"} +SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 8, startChar = 42, len = 5}, name = "XList"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 48, len = 4}, name = "Char"} +SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 8, startChar = 56, len = 4}, name = "XNil"} +SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 11, startChar = 15, len = 5}, name = "XList"} +SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 11, startChar = 26, len = 9}, name = "XListUnit"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 11, startChar = 37, len = 3}, name = "Int"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TDataType.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TDataType.expected new file mode 100644 index 0000000000..69c7df3b19 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TDataType.expected @@ -0,0 +1,4 @@ +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 3, startChar = 12, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 16, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 3, startChar = 30, len = 2}, name = "Eq"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.expected new file mode 100644 index 0000000000..36fa8306f9 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.expected @@ -0,0 +1,4 @@ +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 1, len = 2}, name = "go"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 7, len = 2}, name = "IO"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 1, len = 2}, name = "go"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 6, len = 5}, name = "print"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected new file mode 100644 index 0000000000..ed4b9a455a --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected @@ -0,0 +1,11 @@ +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 3, startChar = 1, len = 1}, name = "f"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 3, startChar = 13, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 3, startChar = 16, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 3, startChar = 21, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "f"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 3, len = 1}, name = "x"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 7, len = 1}, name = "x"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 1, len = 1}, name = "x"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 6, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 7, startChar = 1, len = 1}, name = "x"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 7, startChar = 5, len = 9}, name = "undefined"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.expected new file mode 100644 index 0000000000..42009d78be --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.expected @@ -0,0 +1,5 @@ +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 3, startChar = 1, len = 1}, name = "y"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 1, len = 1}, name = "y"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 9, len = 1}, name = "f"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 11, len = 1}, name = "x"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.expected new file mode 100644 index 0000000000..3a8fb780d8 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.expected @@ -0,0 +1,7 @@ +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 3, startChar = 1, len = 1}, name = "f"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 13, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "f"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 7, len = 1}, name = "g"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 5, len = 1}, name = "g"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 7, len = 1}, name = "x"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.expected new file mode 100644 index 0000000000..3f5196f85c --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.expected @@ -0,0 +1,13 @@ +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 6, len = 3}, name = "Lam"} +SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 6, startChar = 3, len = 4}, name = "Lift"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 11, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 36, len = 3}, name = "Lam"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 40, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 7, startChar = 3, len = 3}, name = "Lam"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 12, len = 3}, name = "Lam"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 16, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 21, len = 3}, name = "Lam"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 25, len = 1}, name = "b"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 36, len = 3}, name = "Lam"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 41, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 46, len = 1}, name = "b"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected new file mode 100644 index 0000000000..99e875fcc3 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected @@ -0,0 +1,7 @@ +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 16, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 5, startChar = 10, len = 2}, name = "Eq"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 6, startChar = 5, len = 4}, name = "(==)"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 12, len = 9}, name = "undefined"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.expected new file mode 100644 index 0000000000..b1ead5dab2 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.expected @@ -0,0 +1,2 @@ +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 2}, name = "go"} +SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 4, startChar = 9, len = 4}, name = "(==)"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.expected new file mode 100644 index 0000000000..657e07536a --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.expected @@ -0,0 +1,2 @@ +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "g"} +SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 4, len = 7}, name = "Nothing"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternSyn.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternSyn.expected new file mode 100644 index 0000000000..435e2d0406 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternSyn.expected @@ -0,0 +1 @@ +SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 5, startChar = 9, len = 3}, name = "Foo"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected new file mode 100644 index 0000000000..2fa8c1752c --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected @@ -0,0 +1,8 @@ +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 3, startChar = 2, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 3, startChar = 5, len = 1}, name = "b"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 5, startChar = 1, len = 1}, name = "f"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 3, len = 1}, name = "x"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 5, len = 1}, name = "y"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 9, len = 1}, name = "x"} +SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 11, len = 1}, name = "+"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 13, len = 1}, name = "y"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.expected new file mode 100644 index 0000000000..6dbccbad40 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.expected @@ -0,0 +1,4 @@ +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 4, startChar = 18, len = 3}, name = "foo"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 25, len = 3}, name = "Int"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.expected new file mode 100644 index 0000000000..8d77a75d56 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.expected @@ -0,0 +1,8 @@ +SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 4, startChar = 13, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 4, startChar = 17, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 5, startChar = 3, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 7, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 6, startChar = 3, len = 3}, name = "Foo"} +SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 7, len = 1}, name = "a"} +SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 6, startChar = 11, len = 6}, name = "String"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.expected new file mode 100644 index 0000000000..0fae7b121e --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.expected @@ -0,0 +1,4 @@ +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 1, len = 5}, name = "hello"} +SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 10, len = 3}, name = "Int"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 1, len = 5}, name = "hello"} +SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 9, len = 6}, name = "length"} From e40ebb8cebf8b3b70a5d3fb3abe15e77ab387306 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 22 Dec 2023 08:58:17 +0800 Subject: [PATCH 39/73] refactor, use ExceptT for computeSemanticTokens --- .../hls-semantic-tokens-plugin.cabal | 1 + .../src/Ide/Plugin/SemanticTokens/Internal.hs | 203 ++++++++---------- .../hls-semantic-tokens-plugin/test/Main.hs | 22 -- 3 files changed, 93 insertions(+), 133 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal index 9e5ff50942..da6bfe4bbb 100644 --- a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal +++ b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal @@ -37,6 +37,7 @@ library , containers , extra , hiedb + , mtl >= 2.2 , ghcide == 2.5.0.0 , hls-plugin-api == 2.5.0.0 , lens diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 43ef9444d8..59c31285b1 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -12,16 +12,43 @@ module Ide.Plugin.SemanticTokens.Internal where +-- import Language.LSP.Protocol.Types.Common + +-- import System.FilePath (takeExtension) +import Control.Arrow ((&&&), (+++)) import Control.Lens ((^.)) +import Control.Monad (forM, forM_) +import Control.Monad.Except (MonadError (..), + liftEither, mapError, + modifyError, withExceptT) import Control.Monad.IO.Class (MonadIO, liftIO) +import Control.Monad.Trans.Class (lift) +import Control.Monad.Trans.Except (ExceptT (ExceptT), + runExceptT) import Control.Monad.Trans.Maybe (MaybeT (..)) +import Data.ByteString (ByteString, unpack) import Data.Data (Data) +import Data.Either (fromRight, rights) +import Data.Either.Extra (lefts) +import Data.Function (on) import Data.Generics (Generic, Typeable, everything, mkQ) +import qualified Data.HashSet as HashSet import Data.List (sortBy) import qualified Data.List as List +import Data.List.Extra (chunksOf, (!?)) +import qualified Data.List.NonEmpty as NonEmpty +import qualified Data.Map as M +import qualified Data.Map as Map import Data.Maybe (catMaybes, fromMaybe, listToMaybe, mapMaybe) +import qualified Data.Set as S +import qualified Data.Set as Set +import Data.Text (Text) +import qualified Data.Text as T +import Data.Traversable (for) +import Data.Typeable (cast) +import Debug.Trace (trace) import Development.IDE (Action, DocAndKindMap (DKMap), GetBindings (GetBindings), @@ -32,16 +59,26 @@ import Development.IDE (Action, HieAstResult (HAR, hieAst, hieModule, refMap), IdeAction, IdeState, Priority (..), Recorder, - RuleResult, + RuleResult, Rules, TypeCheck (TypeCheck), WithPriority, catchSrcErrors, - cmapWithPrio, ideLogger, - logPriority, realSpan, - use, useWithStaleFast, - uses) + cmapWithPrio, define, + ideLogger, logPriority, + realSpan, use, + useWithStaleFast, + useWithStale_, uses) import Development.IDE.Core.Compile (TcModuleResult (..), lookupName) +import Development.IDE.Core.PluginUtils (runActionE, + toCurrentPositionMT, + useMT, useWithStaleE, + useWithStaleFastMT, + useWithStaleMT, usesMT) +import Development.IDE.Core.PositionMapping (fromCurrentRange, + toCurrentPosition, + toCurrentRange, + zeroMapping) import Development.IDE.Core.Rules (Log (LogShake), getSourceFileSource, runAction) @@ -49,58 +86,29 @@ import Development.IDE.Core.Shake (ShakeExtras (..), getShakeExtras, withHieDb) import Development.IDE.GHC.Compat -import Ide.Plugin.Error (getNormalizedFilePathE) -import qualified Language.LSP.Protocol.Lens as L -import Language.LSP.Protocol.Message -import Language.LSP.Protocol.Types --- import Language.LSP.Protocol.Types.Common -import qualified Data.Text as T -import Ide.Plugin.SemanticTokens.Query -import Ide.Plugin.SemanticTokens.Types -import Ide.Types --- import System.FilePath (takeExtension) -import Control.Arrow ((&&&), (+++)) -import Control.Monad (forM, forM_) -import Control.Monad.Trans.Class (lift) -import Data.ByteString (ByteString, unpack) -import Data.Either (fromRight, rights) -import Data.Either.Extra (lefts) -import Data.Function (on) -import qualified Data.HashSet as HashSet -import Data.List.Extra (chunksOf, (!?)) -import qualified Data.List.NonEmpty as NonEmpty -import qualified Data.Map as Map -import qualified Data.Set as Set -import Data.Text (Text) -import Data.Traversable (for) -import Data.Typeable (cast) -import Debug.Trace (trace) -import Development.IDE.Core.PluginUtils (toCurrentPositionMT, - useWithStaleFastMT, - useWithStaleMT, usesMT) -import Development.IDE.Core.PositionMapping (toCurrentPosition, - zeroMapping) +import Development.IDE.Graph.Classes (Hashable, NFData (rnf)) import Development.IDE.Spans.Documentation (mkDocMap) import Development.IDE.Spans.LocalBindings (getDefiningBindings, getLocalScope) import Development.IDE.Types.Exports (ExportsMap (..), createExportsMapHieDb) import Development.IDE.Types.HscEnvEq (hscEnv) - -import qualified Data.Map as M -import qualified Data.Set as S -import Development.IDE (Rules, define, - useWithStale_) -import Development.IDE.Core.PluginUtils (useMT) -import Development.IDE.Core.PositionMapping (fromCurrentRange, - toCurrentRange) -import Development.IDE.Core.Rules (Log) -import Development.IDE.Graph.Classes (Hashable, NFData (rnf)) +import Ide.Plugin.Error (PluginError (PluginInternalError, PluginRuleFailed), + getNormalizedFilePathE) import Ide.Plugin.SemanticTokens.Mappings +import Ide.Plugin.SemanticTokens.Query +import Ide.Plugin.SemanticTokens.Types import Ide.Plugin.SemanticTokens.Utils +import Ide.Types +import qualified Language.LSP.Protocol.Lens as L +import Language.LSP.Protocol.Message +import Language.LSP.Protocol.Types logWith :: (MonadIO m) => IdeState -> Priority -> String -> m () -logWith st prior = liftIO . logPriority (ideLogger st) prior. T.pack +logWith st prior = liftIO . logPriority (ideLogger st) prior . T.pack + +liftMaybeEither :: (MonadError e m) => e -> Maybe a -> m a +liftMaybeEither e = liftEither . maybe (Left e) Right ----------------------- ---- the api @@ -112,78 +120,51 @@ logWith st prior = liftIO . logPriority (ideLogger st) prior. T.pack -- local names tokenType from hieAst -- name locations from hieAst -- visible names from renamedSource -computeSemanticTokens :: IdeState -> NormalizedFilePath -> Action (Maybe SemanticTokens) -computeSemanticTokens state nfp = - runMaybeT $ do - let logError = logWith state Debug - (HAR{hieAst, refMap}, mapping) <- useWithStaleMT GetHieAst nfp - (TcModuleResult{..}, _) <- useWithStaleMT TypeCheck nfp - (GTTMap{importedNameSemanticMap}, _) <- useWithStaleMT GetGlobalNameSemantic nfp - (_, ast) <- MaybeT $ return $ listToMaybe $ Map.toList $ getAsts hieAst +-- computeSemanticTokens :: IdeState -> NormalizedFilePath -> Action (SemanticTokens) +computeSemanticTokens :: p -> NormalizedFilePath -> ExceptT PluginError Action SemanticTokens +computeSemanticTokens state nfp = do + (HAR {hieAst, refMap}, mapping) <- useWithStaleE GetHieAst nfp + typedAst :: HieASTs Type <- liftMaybeEither (PluginInternalError "HieAst a convert cast to HieASTs Type") $ cast hieAst + (TcModuleResult {..}, _) <- useWithStaleE TypeCheck nfp + (GTTMap {importedNameSemanticMap}, _) <- useWithStaleE GetGlobalNameSemantic nfp + (_, ast) <- + liftMaybeEither + (PluginRuleFailed $ "hieAst does not contains ast for:" <> T.pack (show nfp)) + $ listToMaybe + $ Map.toList + $ getAsts typedAst let nameSet = nameGetter tmrRenamed -- get current location from the old ones let spanNames = mapMaybe (\(span, name) -> (,name) <$> toCurrentRange mapping span) $ hieAstSpanNames nameSet ast - -- because the names get from ast might contain derived name - sMap <- MaybeT $ return $ mkNameSemanticMap hieAst refMap importedNameSemanticMap + let sMap = plusNameEnv_C (<>) importedNameSemanticMap $ mkLocalNameSemanticFromAst ast refMap let moduleAbsTks = extractSemanticTokensFromNames sMap spanNames - case semanticTokenAbsoluteSemanticTokens moduleAbsTks of - Right tokens -> do - source :: ByteString <- lift $ getSourceFileSource nfp - pure tokens - Left err -> do - logError $ "computeSemanticTokens: " <> show err - MaybeT . pure $ Nothing + withExceptT PluginInternalError $ liftEither $ semanticTokenAbsoluteSemanticTokens moduleAbsTks semanticTokensFull :: PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull semanticTokensFull state _ param = do - -- let dbg = logWith state - nfp <- getNormalizedFilePathE (param ^. L.textDocument . L.uri) - logWith state Info $ "computeSemanticTokens: " <> show nfp - items <- liftIO - $ runAction "SemanticTokens.semanticTokensFull" state - $ computeSemanticTokens state nfp - case items of - Nothing -> pure $ InR Null - Just items -> pure $ InL items - + -- let dbg = logWith state + nfp <- getNormalizedFilePathE (param ^. L.textDocument . L.uri) +-- logWith state Info $ "computeSemanticTokens: " <> show nfp + items <- runActionE "SemanticTokens.semanticTokensFull" state $ computeSemanticTokens state nfp + return $ InL items getImportedNameSemanticRule :: Recorder (WithPriority Log) -> Rules () getImportedNameSemanticRule recorder = - define (cmapWithPrio LogShake recorder) $ \GetGlobalNameSemantic nfp -> do - (TcModuleResult{..}, _) <- useWithStale_ TypeCheck nfp - (hscEnv -> hsc, _) <- useWithStale_ GhcSessionDeps nfp - (HAR{hieAst, refMap}, _) <- useWithStale_ GetHieAst nfp - let nameSet = nameGetter tmrRenamed - gMap <- liftIO $ mkImportedNameSemanticMap hsc tmrTypechecked nameSet - -- sMap <- fmap (mkNameSemanticMap hieAst refMap hsc) gMap - return ([], GTTMap <$> gMap) - -mkImportedNameSemanticMap :: MonadIO m => HscEnv -> TcGblEnv -> NameSet -> m (Maybe NameSemanticMap) -mkImportedNameSemanticMap hsc tmrTypechecked nameSet = do - tm <- liftIO $ foldrM (getTypeExclude (tcg_type_env tmrTypechecked) hsc) emptyNameEnv $ nameSetElemsStable nameSet - return $ Just tm - where - -- ignore one already in current module - getTypeExclude localMap env n nameMap - | Nothing <- lookupNameEnv localMap n - = do tyThing <- lookupImported env n - pure $ maybe nameMap (extendNameEnv nameMap n . tyThingSemantic) tyThing - | otherwise = pure nameMap - lookupImported :: HscEnv -> Name -> IO (Maybe TyThing) - lookupImported env = fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env - - --- mkNameSemanticMap :: HieASTs a -> RefMap a -> HscEnv -> TcGblEnv -> NameSet -> Maybe NameSemanticMap --- mkNameSemanticMap :: (MonadIO m, Typeable a1) => HieASTs a1 -> RefMap a1 -> HscEnv -> TcGblEnv -> NameSet -> m (Maybe NameSemanticMap) -mkNameSemanticMap :: (Typeable a) => HieASTs a -> RefMap a -> NameEnv SemanticTokenType -> Maybe NameSemanticMap -mkNameSemanticMap hieAst rm gMap = do - localNameSemanticMapFromTypes <- mkLocalNameSemanticMap hieAst rm - let combineMap = plusNameEnv_C (<>) localNameSemanticMapFromTypes gMap - return combineMap - -mkLocalNameSemanticMap :: (Typeable a) => HieASTs a -> RefMap a -> Maybe NameSemanticMap -mkLocalNameSemanticMap hieAst rm = do - typedAst :: HieASTs Type <- cast hieAst - (_, ast) <- listToMaybe $ Map.toList $ getAsts typedAst - return $ mkLocalNameSemanticFromAst ast rm + define (cmapWithPrio LogShake recorder) $ \GetGlobalNameSemantic nfp -> do + (TcModuleResult {..}, _) <- useWithStale_ TypeCheck nfp + (hscEnv -> hsc, _) <- useWithStale_ GhcSessionDeps nfp + (HAR {hieAst, refMap}, _) <- useWithStale_ GetHieAst nfp + let nameSet = nameGetter tmrRenamed + tm <- liftIO $ foldrM (getTypeExclude (tcg_type_env tmrTypechecked) hsc) emptyNameEnv $ nameSetElemsStable nameSet + return ([], Just $ GTTMap tm) + where + -- ignore one already in current module + getTypeExclude localMap env n nameMap + | Nothing <- lookupNameEnv localMap n = + do + tyThing <- lookupImported env n + pure $ maybe nameMap (extendNameEnv nameMap n . tyThingSemantic) tyThing + | otherwise = pure nameMap + lookupImported :: HscEnv -> Name -> IO (Maybe TyThing) + lookupImported env = fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index c2e75137db..b5d979d7ae 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -55,28 +55,6 @@ semanticTokensPlugin = Test.Hls.mkPluginTestDescriptor Ide.Plugin.SemanticTokens mkSemanticTokensParams :: TextDocumentIdentifier -> SemanticTokensParams mkSemanticTokensParams = SemanticTokensParams Nothing Nothing -runSessionWithServerInDir file x = - Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do - doc <- openDoc file "haskell" - res <- waitForAction "TypeCheck" doc - x doc - -runSessionWithServerInDirAndGetSemantic file x = - Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do - doc <- openDoc file "haskell" - res <- waitForAction "TypeCheck" doc - res <- Test.getSemanticTokens doc - x res doc - -runSessionWithServerInDirAndGetSemanticsFile file x = - do - let path = "." testDataDir file - content <- liftIO $ readFile path - Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProject file) $ do - doc <- openDoc file "haskell" - res <- waitForAction "TypeCheck" doc - res <- Test.getSemanticTokens doc - x res doc content goldenWithHaskellAndCapsOutPut config plugin title tree path desc act = goldenGitDiff title (FS.vftOriginalRoot tree path <.> desc) $ From 90cf6e480929f0cc0a355ca1682f078e73053048 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 22 Dec 2023 10:29:19 +0800 Subject: [PATCH 40/73] Fix 9.2 --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 59c31285b1..808b49113f 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -19,8 +19,7 @@ import Control.Arrow ((&&&), (+++)) import Control.Lens ((^.)) import Control.Monad (forM, forM_) import Control.Monad.Except (MonadError (..), - liftEither, mapError, - modifyError, withExceptT) + liftEither, withExceptT) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Except (ExceptT (ExceptT), From d04ecca6710ea98775c3ce55b9355b26f8967d2a Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 23 Dec 2023 06:37:40 +0800 Subject: [PATCH 41/73] add persistentSemanticMapRule to prevent semantic tokens block on startup --- .../src/Ide/Plugin/SemanticTokens.hs | 4 +++- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 6 ++++++ .../src/Ide/Plugin/SemanticTokens/Query.hs | 3 +-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index 80b4a9fcda..66a3ec33c6 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -14,7 +14,9 @@ descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeSta descriptor recorder plId = (defaultPluginDescriptor plId "provides lsp semantic tokens features") { Ide.Types.pluginHandlers = mkPluginHandler SMethod_TextDocumentSemanticTokensFull Internal.semanticTokensFull - , Ide.Types.pluginRules = Internal.getImportedNameSemanticRule recorder + , Ide.Types.pluginRules = + Internal.getImportedNameSemanticRule recorder + <> Internal.persistentSemanticMapRule } diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 808b49113f..6920ba3b2b 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -75,6 +75,7 @@ import Development.IDE.Core.PluginUtils (runActionE, useWithStaleFastMT, useWithStaleMT, usesMT) import Development.IDE.Core.PositionMapping (fromCurrentRange, + idDelta, toCurrentPosition, toCurrentRange, zeroMapping) @@ -82,6 +83,7 @@ import Development.IDE.Core.Rules (Log (LogShake), getSourceFileSource, runAction) import Development.IDE.Core.Shake (ShakeExtras (..), + addPersistentRule, getShakeExtras, withHieDb) import Development.IDE.GHC.Compat @@ -167,3 +169,7 @@ getImportedNameSemanticRule recorder = lookupImported :: HscEnv -> Name -> IO (Maybe TyThing) lookupImported env = fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env + +-- | Persistent rule to ensure that semantic tokens doesn't block on startup +persistentSemanticMapRule :: Rules () +persistentSemanticMapRule = addPersistentRule GetGlobalNameSemantic $ \_ -> pure $ Just (GTTMap mempty, idDelta, Nothing) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 6c20cd30a2..05f82dc51e 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -101,8 +101,7 @@ nameNameSemanticFromHie hie rm ns = do ast <- selectSmallestContaining span ast nodeInfo <- Map.lookup SourceInfo $ getSourcedNodeInfo $ sourcedNodeInfo ast -- usually for a visible name, the type is in the first child - nt <- listToMaybe $ nodeType nodeInfo - return nt + listToMaybe $ nodeType nodeInfo bindSiteMaybeTokenType :: Span -> Maybe SemanticTokenType bindSiteMaybeTokenType = fmap typeSemantic . spanTypeFromHie hie From 16d945f2aa69c8f1690fd5b930bd24293d461115 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 23 Dec 2023 08:14:55 +0800 Subject: [PATCH 42/73] Fix, use hieKind instead of cast the type directly --- plugins/hls-semantic-tokens-plugin/README.md | 3 +- .../SemanticHighlightingSample0.png | Bin 320582 -> 0 bytes .../SemanticHighlightingSample1.png | Bin 320582 -> 0 bytes .../src/Ide/Plugin/SemanticTokens/Internal.hs | 14 ++--- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 48 ++++++++++-------- .../src/Ide/Plugin/SemanticTokens/Query.hs | 19 +++---- 6 files changed, 44 insertions(+), 40 deletions(-) delete mode 100644 plugins/hls-semantic-tokens-plugin/SemanticHighlightingSample0.png delete mode 100644 plugins/hls-semantic-tokens-plugin/SemanticHighlightingSample1.png diff --git a/plugins/hls-semantic-tokens-plugin/README.md b/plugins/hls-semantic-tokens-plugin/README.md index e39c44c55b..050a54734d 100644 --- a/plugins/hls-semantic-tokens-plugin/README.md +++ b/plugins/hls-semantic-tokens-plugin/README.md @@ -51,5 +51,4 @@ It should be implemented in the future. ## Semantic highlighting sample -![semantic highlighting sample](SemanticHighlightingSample0.png) -![semantic highlighting sample](SemanticHighlightingSample1.png) +![semantic highlighting sample](https://private-user-images.githubusercontent.com/14073857/290981908-9619fae2-cb92-4d4e-b8f8-6507851ba9f3.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MDMyOTAzMjcsIm5iZiI6MTcwMzI5MDAyNywicGF0aCI6Ii8xNDA3Mzg1Ny8yOTA5ODE5MDgtOTYxOWZhZTItY2I5Mi00ZDRlLWI4ZjgtNjUwNzg1MWJhOWYzLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFJV05KWUFYNENTVkVINTNBJTJGMjAyMzEyMjMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjMxMjIzVDAwMDcwN1omWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTNiODU1MTBhNTRhNTFhNmYzZjRjYTM1MzViZjNhMDgwYTZiODk3OTA4YzFmMTQ1ZjhhNDcyYWMyMjFlMDQwMDYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0._lgQ0lAGUD5nUkRx_s2rWxTS81ezqKr71ad2fWHGDfk) diff --git a/plugins/hls-semantic-tokens-plugin/SemanticHighlightingSample0.png b/plugins/hls-semantic-tokens-plugin/SemanticHighlightingSample0.png deleted file mode 100644 index 33cdb171efe1235d89f092daa69b7c0be4907ef0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 320582 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

    q< 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

    !K5~Zh>U~e2 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

  • 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ȹpKrQcS&1R&RB9+iwqABKdhky%m-2% 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

    &DGdfQ&J8;&twWOX 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 diff --git a/plugins/hls-semantic-tokens-plugin/SemanticHighlightingSample1.png b/plugins/hls-semantic-tokens-plugin/SemanticHighlightingSample1.png deleted file mode 100644 index 33cdb171efe1235d89f092daa69b7c0be4907ef0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 320582 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

    q< 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

    !K5~Zh>U~e2 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

  • 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ȹpKrQcS&1R&RB9+iwqABKdhky%m-2% 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

    &DGdfQ&J8;&twWOX zS(3%d{OJFn7>D9Zo2G#GRyO3%Jp%%}@+J&`;Y0iOl#RO<0iam9B=L)xCQxd@OL0W7 zyyXMXmj7du$eJx7xm(vHP&wU&N;3gUT89l13UFE^(A}k89$_^9q+fXwuYoljaWyh% zhrm&mHSa`S$H2)?Xzw-p%kHn#+1he0=nFQWX~ljqk^U>__kzg`PgE>_uweh?DpDQ zSq?$Hi^yH6zC$oH(uc?Q|SOW8%bfoWbE zAMCm07Re(*SEly^d@F@ae}6Fk`ddZ@1}}=`CsGs|B^-~tOi%p)2g{29$!6AB(uG)A zmLBQi{$2?ZdURRC2d@KhVRwN-mU3}y!GYcj#QoHxUI(!wy+gls?E z{Y=}p-!P|hgjvu@7AOhb{v?fFcRcnqQQV-6S{7 z1^{S+prH9RT$&JljT)D!Qu1&U5cAuWMjY?c(vD40#-1#+M-Pxe_m9q4@4KxxuC)N> zSx^+q`H1FTNx|h7@BmW#I+z&9xTSVRT2c>$J^_qqZW!iQ4^_)z0buG{y-Z*w2ooVK z1s{5#?c5uug1$}9`9Qwo&J55nNUqd0i&^C_0E^B6+1Mxp{8IPk04TvB}{ z**8-ui&+<4eIN`R9x3h4OO3mZUVwmr{-)Fj(rMl1`JEQ2RS?V}5MU{7`893Lz>l%w z5OS0#A!Gu}Rz{>)=5?ccJ=Bl`pM*-mIqpaK%BQjo5-6S>>Vekj!_%c2$8#ZfRGIa1 ztS)~e^!lIpFfaMmQtevH$UNpq(Z4s5XJ*!hgFuT#H96nR0>9*$HM%^(sv(TV&P&?X z`eI`hqY;=zP=MU?Wj$Eh*RSj2jVEqkh3Z#mbsq|OBJ2eVFU)q0$FhBGN1rB*b8jE1 z<7x;}Sd`-{uLpQcfDhkgY$QcsRT{>*9#Lqmak!Q3IMFxL7{H&8R}6Ju3-FAkx7Er!f|PntMZzTbieo3 zT%Rz=*dKfV7MW~pys)lxM+qhwJjT8C@Abv3dH|^2GaaF_((IXNg0$0y_Fzr1tXAeP zVt)dlVU0l53%b?g7($ihr+;I7Q)0<|llcYkI;lzCw@j^NcuKyT5f#}94Ej%V$cGwI z3DMaS$;i-d{oERVf0Z7#Z*3aD0kFZv-CcR+^0&1LwM#+)F}kfmW6$;s4fBHtZporA znc@+>Z`Q4x3KrrCp3{f~!K{-!x}&oyQ6d&@X#M=$EJzuz;&7u4K8WGIn{}6wOIgY- z1ab05i<2-NKhp%=+iQWAq|*Wl6AmJQf)`RFuD6$u#$dkkpEz)SW6#P0!gUR|Pz}Qs zGVG1MR+A-1u4woJ15m=Ow*y>XPHuUJ1dFQPZ?-tJ)CttzY#PU(S`+Qwx64K5Pvm9K zh3?aJz)1aNn`stOJg#2+22kK;;d8|HMtsX(P?@fr0nXIbF(DJ1F&y&kJAf;V^7dL? z*$2Qe2X{6v)nOv(c1^mo)$VC!yS9^+aBX(uq2JAt8;@ZU>6-8E0*>k2wH+>?09$B! z89Nj7)sg&cvZ9@z&5#lK)eFyIpebHveL!3cBLCk4YL1pqmo^Fih&ZJGlk%wz5A z2fC{5jf|G1SJ#pG*7I+_i0Y%;Z)+6Z#{=TC^hM1sv;=?4>?%cmrObB`nFvTRIHt_$#@BYu& zFi`^2q5bh~#9y;_|C1MShv5$)Po$dqUpx9gkGdA14R8T9ufKo#uX*18@NPc-XZ2~1 z(Z7t@KRxRDP0hPZ$x8U2yZ(on@mHS~fL6rOk0F1jrtpvdP>=(Lf|BX41m6DXq5mHb zmbrS-j@ zH*MORkKC67P{bbd#5$&D46!(1DGpSQapmgQu` zaA(GoY;jj#$gNQdMr#B0_)qkBV@{N#d$xSLAD?K;N;UkJ+wCV*Prtu=8X8R(r;NMe zSUPxY-Svmu?S4TUo@|JLUR|)xfEoLC5wt*|%opBtbql^J-YZ(E$Gs;M;X`~!qz)`~ zoYZlTEF=f>{s`23q7+|VR=4PPzOT)K3`;$r9p^QtzN&FD2wP`uh#p^&215vow8~zM zOw66TWU8b#K3%Qe5UKG^i5hF@3qBmjWw{odJYaFmqOXW{{fn*OPR|scC}(n5R9GU? zfP4>C#fFi7vQA#U*Akugg?!rIK^@Z_nfqb?C+^YOcr~hVR`h$J;Y6#G0wB9ta<4dnE0yNCegm1IqGQ(FQbjZ|c**4*7Fqt1l|GN5*al}i7fVzp<8lHKc-zdUdb;D-Lh5ejdy5r!DGf;%0NuxG8 z#y3w2^~<8-dmZ(}SaQ3MknqD<`8@eB!k?hPPnlZfLQ2qR%SUWoHqwcR0w~FE0R0+p z)Jy1>S1#l1+H3}@9Hxlr8``VP*p~9|0)h&52dSt}9FD6sNNvSGB^dGS`Y--Sk$IzQ z2aTFQ3=hQwSADk$wD^1kv{(m;ufVQ=I$xgdMCWi==BpT)le`ma^Uaexe=m2yqVrs&EE|(}sa} zO^+vSrW}ksi2szhv6#)RTEe2nHL!q;#96cZl-}@B?5d0x3-4v2q3564R9t5f5wy7* zv1UUV-X~6;VQzW0|cO7Hg^wrL0cj?8lyJ$;GqKe=sR4WiTG)t1vwaIkGa92CnT zzb5AED-S-ZGdGx}N`YC{bI*!Pull)LTuPke%TDoX);S4s#8l*o4mnRzwkqo~yyd;x zNKak*PT%hyGj`H{e29d(NQ<`!c|>y0HT)1ZW+P*WOt@vBCUx#klIwyI?A~}%m$RPS zS6O`zGOm5qX&+Z4Uhu?#{#Tg{IY(^6XKqyMTOm3_K2>Fbq?LSqCEi}1^?@q?8KIYC z^pdM&clYbw^-^iJzP(ZOwlJ1XGSeigZYlyL$TQ(^&wZc*YB9qZT+*{(7Ha-(K=Yl& zkZc3FWxoRd0k(tXiJn7@@nuBPPTV;R&_NDn8lKaV*jGdfr0p8lsL4BB)mAAe7aMw@}vQh!*KGT_Xdp!?dDFnx~v=C*DUfF>8Q| z=AYUGpj5T)dY>h9rarejN*T>tXFoB}c6{?)y+`zI+Qt|6qFAL_ktZNqDs*&xO>#h^UPv5m4Yu9P)c#}szBt~1m|dSi)C$0Q}dMwZN-td8b1LFVp*qz>P(6Y zr@A+jgc^|5zQfFrxd8Fe$^zT5k@p0_^z zC7JZ_+Gfo9^zkn)t(B4e^~~nC68h9ZrlhJIJM#hysE*|iTu6Rs z=cA9d9e3n1zue7msQXs21|Y!_r7wMH6zH{lbp|3J_0BWbQyM+f&P|Iu-;&IN$6sA3o{)vbf@?XuC&`Hhh1dEO`&a;cu75-mU(`I41R%nf^7uzjAg< zA^~oMTKFUsU#IJA``YC$$vF_5{iu-vygiC>iE91)X zH_x)zx)q~`z98U~1D#_!x1Md+gXZ3OOjD%vd`?nUUOa7+mOdk=OS;~$5nIMO@3J{u z(Vr5!#QKsXn_f^LtEPMgXo6H((Tb`$-?|wHL-r=Jnw1@ zY|?n|?=26;eTXPR*e1p%ZDBt&WZC@;@t%vgo1l+SDz0*54Z#(amGmPWzf_rNdm-%+ zh0PK^AcNlAR{odzN+1B+S-S;osw?Ac08Ojp>^`}W6peLKaqvAPpZYGjhEx2CPdDaB6lp%+V?v$DTj{$Z9=^1nQ<=;o29aSdHtE z8s#i3wt(25^6y-Vf?01RZ`mFf7Ri@bBBvbd-+gz4YjeNt)lHa6S?NBamG7HWmHysv zSm&5PlCPdXva$a&kaj5xq*XTj5IS9A7ts0bHKQFt3Gv>{o6nBb0Mcl}#(MaDz|iTH z5>-FXAkETVFok232vbLE)B!9OgX7vFsg6ps*GnzV$2)OVoCtdZ-}Km!Vzo4`>S#bE zImXxPs5!`wu67b)Xk_Tajw-H+-GRt>lSws}IqaQLFXw6R#F5X`6_;p#nT-|3F3Dcm zZq4x2P@U=p0a%$N^2Ul-QkmLUv57q;#ov28eOQi$d1#L-@1Z*kqXEYPUmHfykMA;~eA>3(^*bb~@i?fJ<)u1kPEmY8RwitE+F zse(&pK5nN`LsHCG{iMv^NB5kTJ=N@u5QQcui;wZ2@J6vRK+3H!wuy z;+$K6(kiW?d-|gWSADH@-5H!AVO2v8DCJS;8F75|D-ImT==9(j7`1GS;V|a@;seJ2|U2 zUfvUAoN)z9k3m!#BrE=Esfj8xq1v=tn%tKT4Jw2|(sKD+=~!Y05X4Mx{)&Qt7f`+u4{u7j(nMJ+{8CZe3|SlTG@p5l=iqACf#$(Y>mLW&2+U@+D8!=I zpx?+nw6X#qm^8|%_w^+&!qKO zsZMHF%f6r1rg0%_dQ&H-vM>=J*kI`41zrtmot4{_r(x~6Iv1Y69mKcwD+|;HmHFxf zt%`f&2N+%0bM=J4+Mrx$huqce*X?hf*4g5mMbCCOf3od-(^t%=)S0e@NI5FR&wP+| z{i$6^eiHp8{i~F$$0=uSt$6DWW6}Y>LffaD&c~l|&HlomHPlJ1e}0ldcZ-o-({IS? zN+&k0i|gJJ^z~7X6atl1PXeKceeI}hSl^p&Tk-KlKdp*2zFzc8wi^}Yo5VdO(NCl* zjayF&_Zx-Ib+p#Vp=v!JA>E1_**75>VgzGIz9$6q-2;(gxLV8POf=kFY=N{klTHW({cvzr!dXQ{uQLU~0>q5vVjY*d!V^J757$>+>Lvm!zQVnBFi1e=0g7JY%6&wT;t z0P1L$kj=w4M>{l)wEzwEyCtFhSu?=Fk?-cF5aXl+uGRn!vqWLjDi#%6#&=cEq$WLRek~2so6CeP z3f1n|Kj4o^G%425Hs!c<4E|OBz03nWhmxE*2k3IOkNsd0{A^Euxb%bgDQ$n2HTnKL zaH#i{tT~@@@fs}I*sr8?O!dF;tA4*=_grY=2aVpOIe56KOV@cy`08@RyeUcu=Ys3# zXB)hMJRn3)0rzaNT1dpsVA1C2IjKE@)-5uWn6)o(bjD#1>vSsvE51N_FAzbcF6M$t zaNWoB`W*!l&=*fGw{KayUJ9YBf2r=K@E+XjxK2N_b`VW=oVeD>f}u=2bL6(c;2Uh| zdSK|4_?JnOrjO99gybiL;KyiAp6Q7!0`s!dObI>7MDWZJTxA-C9$@TG+d{ic`C{Q6 zC_pr+Y`Rk20Wx<5o~DHx`V7A{w#rYC0JQLG3(7%#o6(Y@iOECboR7Rm1LCcCrghU_zaxC_8%HaP2}|JYdJOR(;5?- zTE>FsXRFr%?63D{HL&fuK$$K*BaM~VwI$3wfczaeDLuN2| z>b-7SPkbEXb#(Ic9^gIOF3Cvk_uv!*T0K6FXIB?}Jx4X*nIb4Hl3W+Q4&FrmYV1Y& zuB%?%J`C2K+fx^;-ojZ4Bm}zv#UWA2xR;p0K1sUBVl6;|H6>#n9q@^FWVSmWmkVJAb zWv63vYX&&)^K0ljnn|%%(LKq$^XXQ0^0xVLX|gd#7~wtU2VKrYaNF}NVT;$v&+3!H zJ18FMQ8q`{9&orP83gpMl>e6&0N)cHm3Lm?Ws=+3(by>6L6ylfdbV2QZ)tg#>4-d1 zj8I$B*|x-U+Fje_L8{WIBkkXc80^pBRA;{;!ii@x+MuT2Vm*vh+ClQaPG6M0{x0L0 zmyc|wgqFH6TL-X7ibC(WkY^u72)VmmFMi8Pi&1kOJaR*3G_E!h(zPSZ9MhpJC5Eo_ z*^x@-^j=Xp?_=XD&F%Dt*Zm-mRW2loKiVLaJY^t1M?Tdx0g@Rb*CN`r!~MF`Gj*F7 zo9sPvCt5mmo{YMWJwMP%K0QR137B0vB!S4Aa)}SDjbA26?*GC|K=$P0@!d2h*Dz8O zjg$Rt>N_LPl94G-R8kX`GXG>OWsA37WmQQr-y}QzZ)#86R@KeW&-{kf=oa{wb%q?AireYDw3qme5#M z|KYlhz@izI>vujPVUhSf^M|C&dzTM_oqQYe`<|MY65^^Y36WY0n{I_NEtjvU##qyW z1X_m5`STk(DDaF2mGD6>s-*CmVuNA+mNJkj7EGr1;0J?C40n*^S?@DcgzG73c!)^W zEjp>YbDnE;3kjLurnB8A#FrXwAl6Dkqzf^d&kj4gYQO_S>eh&&gN=8BFE{&EuiH^T zOXrOBpFC@A9ozE}B38)-`>4>WmASqg`OYMLCi;QrHlynciokr!tm5k@ckyIi5D{Hx zX1W>l@y?xp{&GBgSD}F)|B-orB9x5*l8kplGF)pgRVe5B(SGi6(9P5B#W!wC3QC1f z{9_K}l4(wHl0`RJmQG&`k?;KgGz#n9f@K`IxH^zqd(i{TK5w>qH;A?Fw&@TBMv65g z=*;LY?9}p`52)uf3_koCCqKbhLWR1ewW8tcZ2;CX%cKCiyPa6hNNb!l?pIf6#NvzdjB%)X%+<4esM|q^& zgq|bPYW36k+80kWKl>V4Xc|5Kdz3bsP^-a1bYch#7FYU`q)GOZU1ef5UjLN?TH>?5 zK#!WGh`>C=80H<>h-*2r^`V4HT{_bb8hiaPe|?AZ^hvmwu{wI=_eqdDF479xNtP6; zLb4T9Bzh%}u#_$np73b3qJR$6-D!86?{_tH(OeVjkqoBOo|{A>gb)K=kPMwV{$;iO zvGxe@Xbwx+-seO`M^0!OHFCcFnFms%Z_(7I#lDNN`x;p^8!<@}lkxq{Iz2L=wtD^p zQ4%dWvZp*XyCTZAv?T{U9ig+zJ*LM9IBqS@+xTAlE{|yaSAry%@$!~|*KcF0U9|f0 z$p;GPM)NJ z8V7NVuxmjnB%l5^RdZsrS`(efbC#tM_$J-e%~n}3&MxLXew9OU>MR+=u~G2e>E;2S z;KwiwXF!+s;aQup)k@pLqCWQkdedShxXu2DWy}GTQ(DXJy^<88r-u1(YR5B2L?i{& zO^wsCTlINXHUF|>G{=&wj1iloDv87+l@nGKul^~d_ay~%_TV}mLHoJ&8$7)JwPOk# zH2viiJ0awCTX{6UoWMq8V_!}0tYt2L7i!wO}(VA zFLmun)o@nJwP5^UtyfOwR6oUvgAU(14%B3?uNTpqVpBNz-mo~(9u6f6TdBA6q9RhI zG#1_z8-Fu}-iJnH7Z{3dVDNLhC~IV?vV2%?K9bc3I;7&54wUR`3tits5A-5GP_F8 zbNmWzpPrGL55_X^!UVR#1jFjW$nS zh;cASP_j%R$K*x28I~B6`b+YvH^JN8lZ<2Ih z+GJ#C_|Y2>jKYRjv}RgFvA0#P)|*q2(sCWuV{Ck(gaJ6I#PSeZ$-Nq8*s;G^qz0|z}{TaRS0$=vic-`%o4tAx0iO(?GH)lWd zDTH!?qZX3gXYN|d>-9BE&&BGP_J+(KvOjnC1-jUKsdm4}ad_;xVx=}Du%=q>z zM?80r%6z)2mWpUvQv>Jg4sTs8_t4gsuZN6cxoIk{Vw)Yj@!>74Uvb#Eq)U&YXbH9Z zv0W^#?5(5RcnPL=+1L7Xnii{4cxTpvr*dH>XFc?U;_dN|(K^Btb30lX>k+OZ{3hp$ zhQ~{=-2#5W#m7T!U5#BiWZj64^g!-h(&0nrNVa0s4_AuJs>i~uN@v4c`NLR$)ke{* z`BN=h9R|*&G<|=U$QorhvDTWG)ETD=;30=@BtHBL-}sMhG2n{gdMk<6H?8Uq&fZw( z&sw4srQe@ht_DWSkAAsd^^lA&H@FDPT(`Oq_ITVmVtVIIv4-0qaYNoq^U$ILT`v)V zYxM6OMcJCJ{Dy^_L?*^XJimNZudwqO%2zuqomz`)GCr1!H{Luy&?e&87?5BOtJW)t z&WmkSHcMo9Cm8SQEP68@Z;E6L;x47L9Cvipb5d_e_+w#e;@gY+b(h_~=r^-0-5;Ad zw|Cei+sCVmf<-Gds95?0HeKU~GL?6zPQA3R7i$ZpVJB|zW#lB#YPQ2pI8^`^Z7`J@ zXX*58&q+Cfl>69np|n3wkgJgBR$BDWE<}*=P-7{QL|snog&;FLxEQ` zfg78CS*Kp9hapL9xoUm9ACOQrylHPN8Lw@y{XA&!GCB3w<25m<2Y)N3t5wqr|6$i7 zz_EX8^>GO$-UBbZzjxrbLf&MfAoMFl@>^zeL)33FQ*5JpQjypB_xsQ=>LFR5Ildztk4`8s1-d z79uSIr2(92J?m`Fl3)T;n?tM6`Os}&-{g`}7tDZs9C|(7f?b+gomaR$y9!ldyL^duI;+vB;?c%I6&S|2aVWC*S<>&i9jB zA3pQrbF+j|=XNtfJ`+mwjdo{6K<`(T-`KuclFUTbCN+1+OMaXm zv?S(v`4LgmjSP^a_q(-e_RZ-LTN&ERLY$n_BKxzCjt9QA^%OFdmhb=1urG{t<02}NPI>2EPaF@QuOYaWhP!K?y|X5= z0IB7v@y5om@L?UXZArastMSZnbIm&O|73_Ap2z@YP1q{ESB4lKfls>03bM=Y$??D9jO~dTA78k?_yfNX0ljs_a z;D&R2tme-m-^1i~Q=W@PeoRkqPL$I^Sor?vq$f)h=|)QUv@p5mS!W;BMbsNZpT2_< zA%)9#Paj9PR43o$D?qPg7N`O@9q7I8dNLZ)qdc@c)^&qu;e7s~M{wQeWm~?nJoK;@ z$|hjE#aE5<&hOR!4LR!7F7n^$Go~xRK09Yo0ZpYODP9a|7?^x$!0v)mqy#YWy=U5C zt{T?QNwgj_TFcZuCN7!?jn2xCW-ZOVU%;o)ple>FRr73UR-1_Y7`e-iiU6DtWyGVH zDF|WCpxdF(Crk373xFnUwQDTS&WJIzK^8taz`a%6yzyIWn9Y(S`cVtw^(it>O;x!> z&hwG#qK}5F-Ycz(V$=PNdGC0hAI|j2!9~T(yNEM&0lk@*+-X8!;S2bOihB!9RfKxD z`sI^p7sK!^7c*wM`K2SVA|a;F7u6=h+2qqgn_>C6RUD5>54YM+gGPc>tVy0vcB{C#;P9736Q^tR-@&Wp%knt zsHyd=jBH~}-tC;+Od#r+tk+F=ak>zAp6qVBXqlcH-5Lpo`E|}q2;_ks`Va2ZFxfoZ z%wK35FQ_pyLA@=<3}>{;51uuKIlcvR5oz6_gZt)Y+|v^vU!*V+-%bul2`>7!aa~{y zr4}#AU*dc}$*bmjP{Ni3bU#G81rZ~&D|Qp{6v%8h%+C%ui}hF{<~+a0xK7y+Fkib% zJYsbxlj-=!dbiyjYbHa24}&rmro`@9CKVh@KXyN8y<7bgOV4nMJ^7{+To=>5I>DzY z!(8uZ7%R*EtG>9nZ$6vAnU}{D`||K6!V6YPLwkB^DCYA>?hfsdz5y}c2VY}T$OiaF zzdxdpghQrM2R4_;?5~~lo(#utR;Zhc;mO2pCqL8oo->9^W%*Jh_`|`l%ZQj$z zE5fIHBit;rtM;YPJAO^nxH1{hhsd;Gj?xlJ{ZN7gAgQ7sb&oa_88|!1U@{csom5%= zlbhsPM;A>!eZH1-Lo2eRC$2lb{BwP1JcLPy5!{8#V?6Th&lzV1O_&ptSC}p{dW4)I zxR~Foeo0N{yqRN!>mO%VscShBp_fj%mot!dNE~F5{nRl?tmAOaehBnkzQT_VbMI8G zLOGg0Sg2TcK%TMXfs83Ws3I%;3vzXj@!o1d3P<3wch(9=XVE3db>~`3NudRcis{T; zhRc|i>|WFO>!n5=x}0rAvK??{G}2DEg#Ta*Y-_b9rq?#^WA}4eKY@7ERTe8M>0C*c zLnu+;@hSs)Nl%xV(lhwsc3G(RHi7raC4qK+X~`rJU&aX+`G^%&;(%9)cP+2FEJUQ) z6*$8gVJNk8e%&MWSx%9g2G!eGF_J0d`Lso(Hefe?%sg4<73~!yFZ`>yp>e!i68Ncq!Y{%d@ZP`L0L>Ik}=!dA=X^cMLN*L779qc$K&Ral_ z3THN#h1ozUk5KlIK& zO)K_6^-k<}4GkN5q%fEglb-AXNQxHD&MWZn$%f0CmOcr5<9V5odCUJJr&FAVQbC{H z*qygKiIqJ9mG=Cy!1#(Ir~_t1@U&bbO2)?Nd{yqS)b#ogt*l>s zyJ)+1VaH_WeK)DziM^gQp>n;9kJoar33xhimL&%lYWd-;i(Up#+KJCSKN*A@Mh{z( zI1c08SEc;7?lWI=PJjA3mVKpA@-EPQ>yY-ph z-Rz`)?%!+P4{RIQM9~SCZ+{1j{nwNK12X$wb@NqnQ`NaS?%!)}P6WJ7f4YsR#=p_m z7eupf@gAw5-dZ9sf7I`*8%?b|^*x$srhf_eg^vcJwWnWtvYYPPE$fxiP zYdN09#@It<2#eVMcA~xt0dqfQv80)MusK+$KS88JMGX$Am*4tUw=Uc`9)tgg$dmkb zOH#FYKQJh13e>!Y&DK>W^Dy;cadxQ%Zr`JhClyh-x`Q_3Iqx1%*NW~o5px*1^EPT$ zs4Z3sO(>Rl(el%+Hf za8#+Y(LTU_1{e=YS_M~Ie7t_GpsbO{xGJ93pwUgBB-^Fv5 z-LXSg8F-yC3-E0De|}eKepR{zp0xl%`zCiAO~lWGTgda;hj0=fxRiPZ{SJQ|U}2MG zrT6}JX;&(~2;xZl!Qf|7N%NiYYH`>tRTA1+dTVxgPE$1S(<;Wgbu=coyNt`+iR0!c zTRJtqmX&oAOH7pg_zPTW9W6(T(X*ds+eqh!Gr;v#2b(W}g3)hX%2)Z3Y4zCOv{Hk> ztE1<@)kg}Z(^am*7n0x@657q+koB#8$DcL)E{bK;rhWKv_i*RVj!pI0x88Qg8p~Cp zTW49%5}49cf;fT-U$IY5`46%YdCCW}F-hRvAIi*kY*pX6LuuNlpt$n)yU&;`&^5j_ z!9|Vtl%kC4TeFShR79%a;Epu=B@KI9q`h{aPaxrbK{)mOHJlg8E#@m?O724J##~Jj z6}P?`aIZ(U$AFo#gc5^{W37l$;E-QKcii$x^{g)uQ zW2`YPcFp(lnX6EpJ?c}Kvs%oIhzepfx7(`!*JhuYB2wK}ztH5mSeI9Ptf#-cY-@#4 z3-*AC?*S>jPlJu+eiHg&n&w^)WGu(}u}oCv$B(7)KbUuO$U6(af-mVxe_j(L0A0PS z3i-WwU-O`21}~TY0N6P-XLNZAnZkvU$%?ljKTFY-Q_m{y?2@CV>ST-g_2;&bpqBcw zG_9TYa@NaO_ch`o^9eYcX`g4ypmA-yew~%liD>t5Z(M=Z*l=s%N^^_H0bEvgZ->d# zeaKZS!UfZ(XgQpLu{|f^F>Idgn(4~m4<30RNi+T?Q(^nJNcT*K8%%^y)Me@0{i>>3 z%VEC;Q{hoHox|F;mM>>wr$1i04@-&IeY6>VAcTtv4cT_5ec^vD8hyBds`y3gKOdYJ z9&LUwO^Th~^0Q>~qoVls?HgTj>S1PwWQps^2mMu? zbl4-8`J8i0I4H-k9j^|Ki+N3ol*DF9`v>rX;3_~dqq_r^M|g2>oQ`H<=&3l^F8OJ_ zJ?5Rgm5jyT8*}*$z!NkF&BE=KIfvUR2(FnbQ20k)&2$>E5Uqz^7r*^h_H96AV4LK4 z+c$>WUY7h;A)8O5O~fAHc574>BiRDVl#DwinHWZ4>FC)xipA_UXvUt@#;}V1AyqW z)BR`IWyj+TyIOP({mPhO)qOq4K8KEkDU3leTltab^iT=VjsjT)ka8KWw>37B(}{^66^=f4bw+r@$npq5DHgeyk6(TEDmcR6n0ODg9Yb zmZUM< zh(miU;#XB6-0nmPCD+ffl}pI@;MKbm8U&8YutV<{biLw5GQGp8m(9?Wd*E>< zI)Y^{C;z*UP`l}B(lpJw;=6q_#CUgun4XlqR%jM)sx9DGlX4sYna#2hJieQFi-9g+ zL(~}(&Pws7sF}u1Zt`XAV(*Aw${35z1z+n)8;8q-GilpHL(F_@LH)i77XgHt=cGFy zD=YEi)-YMALA6~l&5O|P>u_x7#Ygxl$E!!px=gV;MVh@FnkL_VJtcx8IN*(LBky05 zd%UL{TDUz#f73BMT{N)Jw!lL>Pr+2@l9GHZwPkRBM{Q-#Z{zq@_%B~B8NcL^4G!&( zl8GiFSFowaM9eo9ycSBl9;;;A-yR!2UvzYr#QIJ(dQ?SP+^E`XKwoJ-qG;B3;`sry zV#7F`+7A7n#@;e4s`h&uR!~4%C8hO7xDlJ0Jh?if0x5hPSnU?>@2Xz4BqhZcq! zx`yt~XM6wO<2`=Id;jkD`SW4Ev1iY<*R`&5t@B*xmOwP?rNSuH#C=*9>toH?m_<1~ zKFm-{EbRvksGZZfJUW6Bn91)` zY`e6c@5N|Cda-;j;)}gzosyo!i3l(6a~Qu!Gm#`ps@ ztlsZ<%d!i`Ua(+w?}heHokWkG;BLHd9QhE)Tr%c%Mie3&%MjY99xn{V@1cYST<2wK zEeWWmV8)jpn65RhKGZ|W^KrLKcVRDG(SjVO>bvtvF#5Sn?MYNP+i^bAzjC8kB7?c4 z*7~xlixY;K5mGKsZShcyw^t}IDB<*U44GKp6?NioG4o>*xQMWM%cf1{;7cTT#B>{| zsnl)n0^gq(ahk}O(*gos|6A7F<-)%Et*$dMX~YUOg9stSi0_TN9$ljhBW9P{osxVy z|BA50RK^S&jc|`DO1rEe$&;dotncA(>H#f#xfYRUIeC7 zT=J-&m}L_72B5ag$Z7BmS5Y8Pz6X}PX0;#Qwl#x<>|K8+Z1hCX~;I(HCCpTC1DP&@~Y`m_9*uh(~;*9!CJT#zCX0t z($yq$EMStT&m2HHxF@Uf9h~?G>K2oKAPLGJk})e)Xy{+-yJP|UZ^ zgNkeqd9BQ|h(FAQWd^}rmex5lta=kxG$!CuGO?mxXjJ^|nI3IOX9-4LX)RAOkhU6D zvXO`DGvQOyiI~@)(NU;P?o+5!!Igf5*Avj5=@dSaVvsDEV4h7za)Tc$5$ibIC8^!cMPl{$P$vgB#pf&Riv+l^^Xb3h_!+~fP|EQ5VX?6Z0 z5U9KpWf_lz>14ALTCqjG)?d#gQCaT1@wcEp&bm-T`H{yg{pXPO@FeP9> zLqqv=&Ms>n_sEXpLGfwU`gdCu(qvbg%0JA1_usw7VpV6qHi_ z{=AYwYf57EXGml;UuPmVMbZw>iUY`}M3z1;`WG~(3%lO|aowUV%A_^~MF5Xh*ATl6Xa9bN$X1Qd1D zRaTaet=a$|?R4Z9%YW_927h`*+nK0TOU|Y!e9}%!3>#qG+Td7-MR(F?Y^S?9M1hi0 zy1n@Y$B85oJ1kJX)0sv>jPV>*eVV0qk2=yZLtC-TEZ<^1AVeELl5JnSi9fId+aa=n zw=oDxwwsB0AYfpkhJXScrL7KS%{$0(b?UJN9)(-{vq-CZ9uqt$1qGtl@+_Hq;t)*j z)BK{UJ(LMI^s>owf6cu`X3hF2e-2Xj=(Kt{Hj+Bpg%1r8|*o)lnRq>3d4C}>wWVn)D=QL@DO;7;SVm@qn-hDn| zn8JMJE1g3E<+KU%x`(hb{&mQ+^Q}wXx#T#SZACRikRzQ0S|TMZG+RMX#rc>HbNLZ8 zukA*8O=T>glDh^;AmnI(kPccx=w(T3t#*cp7fx5WvXmONaV3p7AtJQWNbE{Lbf3ov zE`ODPptJ|e#t~`KklIp%Le2!>S%wgV(Rk0KJr>8(2GpxBvASmLHE}e8hV|b02{dnw zba)lbh7xWvxEM<~dX}5*eim}R`=UAg_LddkcnDL!y@eG#nzN<68h0QhrCB@pz%Sa9 z@h2NpRrM>0s>Gfb&ROuD>v3$?Ryv&Pl_C?0{=FgB^Bm{}zh3!70;MJs{1C#w|78@3 zwp2>k$_{jCoP?oYzI+)^IiD%+A)D{t(J}}3VM$H^-tE!L!S|;Wb^OI*H=Wr#h+-Wq z^E8Ixmn~{=)B8aB)J-=keZtUV6aU*xcuF`JQLOcPH8$i@BQ=CUL+{L^{Q=9)!RNf{ zn=FJK47>S;3*QqW>=)N+K_}&IGgIHnEjq!s!XHTx!z42nx;h_5_SOkyOC4IjJ5#M` zPPWyd;e?)51+-m@f&)~L&>D8(zjt?oLxd>5l+Q73Jl@f?P3Sp6^9TG&hVi#&Q6aWw zcE~d$_%$xXEEMxp$0rDA++g{;D!a72tR^37&`WUaX-M93abj6r5*MLPCGqES#{CsB z>lC`TiyCOi3uhW1L&FEz0SEC$b6jPa{F=Of+uL4N^6;mtDlY^toV1)McVvU9oC=*u zpsKu4$5Z070)e39*S9w~(TPj?o1U_=^fv*Arv%i}EKPuC>47Eo=R7*UfhGSA7kr&E z;}ob)4&U|f^Ni1J^Fv=bAB&%Kn4xQF?JYISI(XnR^5ZeNQ)Xx9%^`(de-) z=n`54BJmmw*0%KT_U-hWobq6RnhP+Yf9BS+l;*c@)PksimD~TpboQeItiL;Bo)3$q zs-?a$j=>>e%VRUBD;B)y2tZ#dGY@x@e|z_(oZOLT+M>krxM%A{$OG6Q`~Qpb_K|<1 zyuGTK*<{v#C&AuR0*w)eAA>ePvXVX+|IopWdKu}$k{~KdKqe^B(r}y_QiqG1vxBDB z2T$TI(3fEp#*(*9Q^t<>CHpi^*LGt17yK~)KF-LGb9Gv27tk+e{;rSaCx2<~TcL6OR3nOlkmCnKBvCbX zKqQFgz^dZt%V9o^FNH8+?6$>Yzrfw827_|>^SA#KHdF}XXQ`0|1Dj*kjBkUYutOua z$pt>d9rB<7vKB_kDM@dJrA97a(B?J)*oCU=6}^c|#WR03tDyj>B$qiAMwP3h#A=66 z=m!jLYz-L|=5b;0QS}T#7)HPX6+c-Q9rnE@aw|)gh8i~0RhhYdO(a1!lw;(i-AUAp zhslY9<+*V#hff@d`fw%%umWEQ%SOIV??x8X{?;!Le)3t!QNYh#;@~g^(7l=6&s<}n zHhzBmN|eC%VR1LY($E@6CZ{!1AAQ0Mm6}uE(a~LwbK{2`POkI_g)3BXhC4DRawud9 zR`-s)zolEMP2?Hq6p>2J|2tD?9`Q6JHCK8F55YaEP$9 zhHc07&W7dBE|38D#H#bwX`{BI-lZ1u>|~-u{=s?c=b@Pv%6satk50(FDnTtt^}h+n z|3ZX4q)W*8gYrJ=_$m1J`iOH@K=DlcMLQcTOQf)N2Tk!&PowRX~D?DH*H$fZSsQY`Ql zmb*dVq}?F33`gl^vzv#D$^>5&91Wsjm#d5fpfs3JYGaMEXxwy8Z?{7uYJia)UQT^vfZ?BVUaSf4PK z0wW)ehD{s09|7-rLg1CYI63ZqTpNmA-PkDZOQH!-V=UpgW@Wc!#Znh*&n#K_Lzac@ z-U{_P%qkC4AZw3HCXoIAPV6pT{b9zhT+VDcE`3~x&>l7Omkh`=emGa-rG6VG8(}2r zhxNpUSJ>sFxRh`)@MH6xrSESgle;C)KNXO<)SU$ zW_LNblM2AQ->$K9IB5K!7uUXGO>{fIJk}M_B0@tAmMa$9Yn*Ev1imu#ruTT zA^eot9K*&cj>v|H!xDVU_|l6cq*=*)V&uIE_7as2PpF}VuBM}M-;F!QT;27TjT%}c@lOgrl8Y_Eqd zSDA(5L3;Ya3F4`?{ZxV<*1Qu?A-jyx#8dt!AO9~s%%DGvG7XWlDn*)JD%!I!UCBb8 z)!)-J;6)6zMy@7=Koj8*%azzb>}J@G*8i1i7kdo=Qq9*wbe@f&6-v7^wclfQlB&En z+dO$&*ukIO^L?r!JT?y0W`2OL!PC}O({cdOpv7OBR6Jps%q}PTFE{xg0C?8rk51q~ zgO=*QW~={i9&0ZZ2Nup?1=O#G{qR37^KXLv#RvdZZAE0EZT>IT4!E8Z-k;lffzpdp z|GzHIpDXfZ{E;o(5WU0vzyDO=9$oAS>utk}u7W4W$s4^7m%#!W=~a)6Qf~W{|5yKi zU;+pY5>u?T0S-@Gj^}@|0Ag76fCwg$f^*&cDRe;U{lANA{!?CY21rlV=s5s#U=A zwQv6=ih?PW{&1edzzrIQz~3VTCz#}S?ZP$Q?TP8Puq3x$xLd#8&hd~19(d5R4gEH< zQwPH@L-emlm)T@DCy6l`H=}XiUwy5trhoJX{m*wtRJjCPslE+T_T8S>-_mP!8kYOk z2#QvI{g-i8X20=&Sua}2zZX>d&w?h&!7K;eD1?z!%v!A9Ca3eXSQP`9IupP44!&_W zNM`leeSw2>!SMRgZ`zT3W3Da4_wQBO4*oA&uEt~phb59y3F{$`#;dls5Zev?2IeM5 zfo=54RsG^5W-3!&7~`JFM#_!y&MHWW>-bD?@~=<-rQ(=11TetSk-yI~sJzFvaD419^!tfoyGJt@oOU~lT5zbdhX%}2W858B(ae#8U5Rd|KX*{p_DXcrs`gm>6?}OWcz%jf&c5We1`eS9KvDUC+(vSDU z%1K%N+ThLhPqsvD_Pu!f#-sR|Oy9K~Rn^ZgJz_Dv zhN+^U+{l-k8_SPvqcsI$h;!R>=GxO^<1+SKS5HY+DQS~b%@ zhY0d23eB6mmPsT|1XpY55{0%Wixy<+=Oga&sQp&q&~{`Lm9h=9Wax zAa4ca#K7`j8o{*gfQ}B{f4r=m3cI|b&|E#!PA<)uss5-fd=|CVXi!>VudC);@m{aL zi~5Hv50%16miI`xkL{MKkm2sO?yR(Lz>!R2Lh8$Ro64Ggmtbt%p#svej6KR@^32$o zEUT6QZ902ZLd4C25X@kd8>+qo4|4VuKa{EJ6( zRCywYwZ2f8&TjrBBc};gx8t0mUvH3C*LB4C<9UjT+pn)jitx4(&38;w6ef;Rr$c7!bP|Wm9GqPpbgEe-x zhE2=I7kj?kFch}>1S-iYKi6g(tD{60X0c$EuW5AS$ahn$<)HK`@?)g)k5ot#aMkYvD*luc9VAdrrlcBo%}I1 zw%0rJ(udH8KfeNP+XOcmoiYcC7+?m|4`+BEm8 z&U#`1AN||v2m8^(5r~59ow$w-($^&guL?jxN&5vx*1FsR*d*Wm5%!JMH0m{iwj3Rj zLBpPUm>k1n>AdxcbF4UgUpa|2|C-%Q^X1|CY(80*TXlV=_U`mWV!1xvsl$VFJPuJ= zU1v*Rlo9a$dTjXw*JDq_>zDc?4h~%_QeYf*Armtdq_G{#$5djeP?Ok>S{v`dgFh#I zOo-jCcrLL6yoQ3awnC?94b~5)GaWse&mvv4M__NMyJ^&ua=5>$)FZh|%R9yXH*jR0>aWV*v3NHsg=b!wUU(e5s zC?+-H56n3GbT7`9Lcy7E&>e&&O0W@LajE zo$f;ji_TK{iaktK9P5ckhVTX`Tv{RkSJ#R|l`zY}Juqg4Xc)DMdw={MOIq^-=5u!v z4sl=$sT&)T#!=@_X|Zg$N%+H*0LlZK$F{8bOZ_X4_jj|JW=Cj}5==pvzC=XS%Jule zdq{;RH!9)0CxjB1m+HWpNi;;07WD7Xd-)#A47t{PEnjJ3thUERAv2C_!o82f}#q{b9mQ7Yk0^JyZK9Lvad#&0|l0|Dzvf4$@=D_E{+2C8y>-)Q_AB>9BC-G#?eLJdY%w=eEn^cbK}2d z$Y{9M)|qmWiA|bwW61k;Tjq#o9x7&L_e}r8vR+ij2P^#?u{Qt0oTG0NaYenozNGB% zsjLAJXW7(FVUkX2Q2ivq@MX-tZLizj8%ur^@`;2ptiF%UiGbWEd9ej6nBQRDG%zR= zijzUWl)S(tXS<3W#`z%tg~gIPFHRaqqcQ0>tG#`jPp`NXIH=Y9eQWy{xL5=AK(Y5W zJmI4ci+1h~r93A@*yeC2BdCvCIflW(_eH!_sCRxs33GC z_v#XzKov`JrlQU8#xQb8z@c``fLCw5s8itQG5Nel9~0IRb?4nLp5hE+@%383IH5DK zeE(64i3#-Y-FCwrd5lxw&c#)I;uJ|}jleXVw8X?!o!5xGyGw`Ty^wm?;5K&0^mUWp zWX1Aop*u*jMtgW`H&2#jNP9O-vs9kG@jTnme{hA$|L(x7wY>JXh!f^MM-{Q-B%}*D zv3N)Iz0h{7P&H2`?2Wg?>z!#ywKheR-`Md2#yamVJ0Bj~JB~yzZ#2O4D2&kB*YHhH&c>b?Dp6IQomAM(6c+Pz6{%5!w;H|FWK;Nq@P~K)TClB`^lxtc?m1C!tR#HebLVBVR7vh= zd{~6udf__;veaP8MM^6qp8u-MSJ0)kS$)=txl8+K>01GWcC`zR)g|_m2Jc%jlxyU6 zH#mU2lEONjWh$R_e(sGQzhnFu1$|xT0y} z$ncDLh$BU11h9#DNrX_t(z2sN9J2Y(B30>heyM|0dEe?uxjE%p(f+2q0Sjhwt|iDT zq{2<=TAxLa^c9_M+uX0x7`=$F(P}mzNO{xp;He{r-vgWAcPSR%3LJcxgYG4V2(^8U zmW#f6uro(X*iXv+Q4Q32gb;F zW2{!vbT2;3uw0axj1c(p8Qwy(@<+wSW`RgD|2Rp!&a#SwqPmWo=I64R)Tz2z?dN*; zU=~mWdO(c{9`o~LgC_{Wl3QE73v$B6*VgeqqAA`k+^+`WIJLfm4Y2n|^uXwl9PNhR zgjdcBKM#umJNV)_p$oNX0DzG_pL@VkY12Ji?bRjkRS zz7b(z&-|EIy5GEcG;nfxx7?ac;U*gNcJT%5`K?I%@Z_^OzamLa9F6*vr_Tr|fdj94 zjeFF82TLz{B!6E2(wVMqse*tLG+g7B7}9Rfq@2bG1065A9x^EF^taiz?;Gf^;o z(!9_@3_Y4GQ#uvBR6Hffx&35DKro!Cu(V4_Bb<$P7x)cX7Q5ZB?yi2H7q7=yV(;_m zBHg*Wi{NM>pezW7=Qa?uX6AZfzVoEfLon_rnIp^Yn#v@gV;}Xa2!5;O!Vx2Ldm)K7 zm05h*^@4yV^Ww+wOMws@!&qNq=PS%M?$%E;E46`j5%h%yUCVa2_Fs9`PY&Ikwyzo` zf^=)byZBdDqFh8U@|H4BZqAgd2M^Q|+7?WDZhtlZqtsk%llr^#ehoC^qTiD7^f2LD z65y@<+_}`mA)UT{LQN+l8OXHC_QBQ+`}vY-OZaDV!cY zocn>KVX>no8%YKFu~Dw5;_ECWeHpaeCPy80;j)jfPvi%(w8R*Q^{eK33=~?P)X@cA zP`#k=eWyBLh+02UpvElI94r0-n|e`#`+nP6XYF!VJQD%=3Hr;y=@!346mjdFERAXJ zy`DT~(6#j7HjTHnR?sGoHSz#17<55sg1MH_kfJ(CDFX+1qowS2)hDE6J<&_g=bG z8^y)zKvaCmZwpU{B8Tp1cZi#-e*aLv-Sb?Z5;yWc#qZ)W8=0|5@tK}AF&X|?Y19NL zFlDo{4e2T8sa*~DSb_x&AkCVEdhYNqtR6dLw@l)Nisj-{V>#plYTEDuVu!LY`B(&V!}aApDaE%Lgnjvb4ovJF@ zitsr>Axgpi5Z9m`s(T~2U@HWyt3u>-eA;J!exbhU2g6guQ2zi1vh&UD=FD?Hy)y|8H$N6snOh2T z{?Hsx;6!Guvd_c@wml?J-A5ddmPAQ$RavueFtMpp=~ak5$=1)-zrLy3`P?eOC;2p9 zq=)(t|B$3}BF(5N5#?bM`)$ZQ-Fu6OZD(|FEyKo4o-eHJ>FDqccJQM z)EZg}>v;uDI%?^$=}qa$&d2);DRr*>S6$3=d^dQ0Nur@PJ@b~0`$EI7N4|b$^0~RW zz59Kg{7;^Hu}$XhRKN(RiGJGOBKM7*I_4QN4E+#Tz1gh*{a?XZ7hpjA8hWYn^-1TK zvPh10=d3-#SG+BkqJp4>B{ubHNmyFsw8bDpC{=u-KQ@9kp7+kxn%f5rEyEL7awc zoiVnw0T zRFffd>!<6b#Jwh~HsdjB-XaWNY~d;Le8p2AnV^P6mA{L?*3zsO_13`C1aeH=i4+DGZy&aE^ zRZ+2B+%Jzpy)qzbgcXOlzn8Wv@Fk*oy{I*?5ajc%UtyjeOa6i*hs2$M^i zNR5GFNPqf*?;QOjm&+Ui#wCL5*{i*GBt|8Yxd4y>w(ocF&U{JTaiDCfrT0fadxfiW z4H{n{;A)0NBpIe8OOIAmRORsdBhTO51%}q#+T+Ir32gRtKCV8DozzOJq@35p@$>L{ zYMcJ)dT8@Wjl;-;)8H@jBaDhv-;Vhl1Jgxqz3X<7q%j0e6=?Ke3Ub^g;~eWGYPEtOb24E% z=eGojDdQcYR-+RH^E#IL?`A}-h@_P=f$9FFd||O=J^psmHW_BR?6>Azagu*A9!KQNj3^m4{GtmX~Ym%2(_z3tL-#N@X2&IkrZyTaKX>O^^Dw=;&; zYMp}_6~kWjZ3Y-tbXWd#UHeLLGZ>78*v_)1`+$iStKDn>NEE)O9i7d*IRXQX#DVSW zlV|&F*WcCxZO34rG4*b?;4jH`GUt#2CVfv*?3^nrG`~K64Q%%gFAq&Sb79kldHGcAvZW(xa7p z+{g9qM!YNO7^SgBI-ryPB8kc)Yi4NY*2-ZZnKLQQE5tRcbqMLxmQr6uL2NfW)xZ)4 zh%fo?0;7$pX7u9Hisf`{#`^A=%!U~i+4jZ^d>E28CNg25!|(9tHQ$3wYS`{PLwZ?6 zHaa4OXAb!2=9%JV?U`P(Oi+lvnYr0RvKHRu^S#RBtZ`@;E^2O^88*o7#O_=PJY8YB zcIP#9$DqMeD*D>VP&zp2hF>`8>7-Qrt@L!oo+le^N4V*3z}DBYm&5>>>t?ejcI9m& zTw^4IrEx$VN=oj)9+|*{<1QRcly@!9KhFZ_a~JkG5dY0u@dgu^MPFN!J2i-7q6uTN zAYH0S;>dkN;Q!v07dzwLvmv(u8?&vSaN_DSfnL(OMzk=pl1OQ#EE%M1 zLxkGz(;Y?R4@NCsfiS{a`<{<^Pv798FTQ)`SX-Ilj%|Xv>fgaNOgrB!=Cq`al5V<) zrZTDO%7rRx%WWV+mN3zjhH|?z=!h$szss=Q!I50L)tlfmi8uZ=CE*NV$up;GHj)Z) zdSO?RWQF!SX>I~^-8h3}@cl(+?hBj)V)?$3qk$Ot3$=OGR}1fmJ+aY85hj6>Jgr`! zPTzd$MA+$i6TEAC>Wehdaj&9vRzf63CuygrqxTZIbpu4)nAn>KuyRr*fGA1}fwkzW zJ-k_3TJ*5S)_m<`Q-32(?anjE>S|soCbjxK2%jh^#h6$cG_940O?NXchrqmV-BqA3 zy!j^Unr`F}`kHh))Y0Zo-7iS+#nZ&koQ_D>u<*bHkr8heHuD6UO>F~>$t3BP6i(89 zy&TucFnLdcBL9`bmJkS=Q$78GiDUcm1|-f-6n5U!jy^~T1?kCPYe3HYS|$KFP+GV1 zNu9^NpVrXJs&+?Ea(jZR?1gGZ$$J*jfzK*KJ3t(piT2NVwZYbw^mc?X z0*;JMB2mADHf;2}B$}Xz?prk2U2Xir3a4Mu0tevkS3e+^~v+-yHiq>Ae$n6rjYEhAlM~& zI}KSdxJaOaJ=%aiT76<1*;{Fh>VF%SRTR*r(TqI&1Ss`xO(|AZHfY-@K4Bc%@Wg1` zy#Wb&q=HP2;{m-gBJvWK)mWeE=)u{Bpe_6))%dr({3O}F99KRrd? z^8yUKkB^K~HK$g+9yjc6xgT!dZ-D_ z3ImbkV+A@rkN2JAzMja}P3g$8EJ!emNqA8n&iZIhn|0F(u_LkFOa`VpIwjo(?xh4F zHf(1xrwD`1=*8k=uTIUIq1|dfL-|6HW6yQVJWfmFNXu2Qcpzz&Sw;O!O!>DMwv>1d zREF-(ukWQ&!y@vglKd>Vf$60K!stm@faLy-6Z#TQhWq$xvAzC_Myiuarnf|N|&YHacnR7*}Sk52tMP@Hr z5iwML`VFQUczj`hqVly%ite81*-%%4Cbb;OzCs6q+%XEL)C!om+t$@#m%#hJCzv9p z;xLyDpS_LZOp@r?7_?bS3LSwoD5T{7HFRl?d;O85Q@>Vw{+(c?v3xO;J0z^gF=M-r z*-IThQRc3n>c$3Ur2{c`hUur3pOYRLBNWfj!Y4^Z1UX+oo+FF{jfj1o`JM1ChFv#H z4bKk|2Ca`k8`+_r;Y?WIgAsV|b-Pe+rq5O}l^L_}{v)q|Dh&wa{QC46G$95yk4dBc z4m^zd3CxZ*YsdJWy$jD@8y-u%*x)dOH-}>E1VTb>C6_Ljln~_%~u1o&mf;JN4(4 zYvx_N7qXiOvlBoD2C7S)@a@-tI8g9rq9iYJqj;mGv7ek5fv#hJp7DJ&Xo|uGkTS0A zJ>aVNSfM?Gj)m4AE*Y7DIFuG_^@ud$FvEUH`jfpX4U_I^96S?XQljt&A!~-a&OpKS zDfloqlWK{%v@>MW_s`DDc-|ta2>1_yT&0^RjQxAc0U5cFQaSRG(%41lwe8kYsNMX6 z=);}vV2)lh4ScF>8jIZV<~#9LMy&T0&eQJCOa{9W+#jI_R@`OWGeh>DhM`O8wo_TJ zY!>y!+RaTOw$-`Lo>gO4erF~NY~*28o_mq3O0V+CK(b(1ALmX(>zQ^Gs)iZ8Mtoc^uDV$)3P4noq)Aae4KTbo_ z4Q)4Uikr$E=`~x&9EXwg4VvsCtE*LS3_6JxOp>SrI@7Xnb~EDzHvF}1_Ei$C6FJvD zmR@Fl2>d?z`#7+Ty^|>GE6G==>d=7;D#w`LH&Hn9>$qF8!^5G9fi$Jmt$yl4^eJ@W z3HL$KA;S+aTJ*pdKM2p{1;Z zvOf^kd~W$=ERX*u6v|I1LmgQDG1$^K@OSfeXZD>LY?|Qt1&*gI|L2-vkP?G-x!Wc< zbXWvcVOj%q0)U`7WcDogXCkydu`R>Re z2>$X|03Gq9_-pH$0dBM(bzl!;Xn;NkF75(JyN_6zu`Bt>L{s|%=fV4yQj&N!<3hLE z4R8igg?oijxqg|>7ENZyb`etA>Uhq{fDBDM>2&(9nR z@)?Yd6{IV&kIgK5vzLo!r!SiOheFCo7Q z`&e7TjA!UB@UO<*r$kkDbxZAMzYBmBNfb&aDEeP3QK}&8^6@{~maO#qN8o6H7LBjx z;uCd-^0{5B-3L%bZp@8Mcd-D2a+(^w(4w%`-V}SSwv;2AvE0%$@NIA&DOZf2^rYN6bawp zkObXs=EbYs7uQO#+A$5)cwri2l{5bjDd^2qZ)OfoSzB|J8r@uTTS+|mxqoKub*O3`HH}_;87xNUGy}zixUiF35VUN%c!7P zFt%-wYh*4pe43Z7EY^Vqn^>e{$!P+Gh?d%0)URlx73QcH+uu;GxcnskYVqQ_RayZ{ z#7TKQlyDVPM8|{e9vujuGc?)0?Yy#)CXUU_EV^5mrRtSpJ}QIkeAmfzURQSbRgh-R z>%j|%aDl|Lt)p!&Y3lmkeofV;7k>*v6v+q9PtzS^y70<1}_pkUc-8|a&)^9ZW zBp>_19Hi~EvMUtsisOsNbE6xMM@Tc=?P{4&M6H^=q}!04mFq2a8_13a-68fLPF6+B zvAlR8`zd&DX0UiS@aoWSrp7%|2fV8Zw=rY3u3RSKT4qAf&$`#24`^>^5@^;7R`n7| zplef#NN6qG4w63l{L7{-Z%%Iqbg8t~l~X%~+?|?u{R8+Rk&VH;RdY5Z(jm$yO>bvnJGc2Jcwkj9R`d2F*XY|z z)$0CQOSg5X8p5vl$GHII-ZT6doPHpx?Q{eGsS~*u|2wMHoxgJ;To2U|{XeI4^{2+T zJ`^-jC2%v;+}*~%mAuTkyD2l$bn|I({H$GCv~FKs+^zI#;d|Hd`j%E;+S&Tg(a*bq!(!cQ4TxlTlp?&?C5_Qu4ybz zycd*ul;{nG`92A()|9$;_UM(bcTA@9YQ<~M+eDRHJPA812 z*ieIK!Cxnw3RGnww0k+hQ=SCBP6TmAKU}*oVnW}0+>mwfEyVG9L%7l9E&WgBvzy2K zB}uOmlk`%VXLE^(x7{N1L@h+z$-m2(x#VLpdo7^bwG?z{vl)vkh8C{>`ct6qZi#z% z0p6@m4@tP?0sbd?z+W}0!d6_m^YV&c1xs&3rK#9PuCYm14)I=QKIP&CwMlo^0JVnR z#Y~qNi_2y^N9KO>aIX8Lp^xDpaG#D&rn_=Wmq3B(d6|h^0lSI% zv9@UNL%ogdrC65+4aFTFU!Po*zoxe1`?DVa);SpH3_z>G&cRekh+=@KZ4Y)5btW$h zph*rfN3F=Pz#CRN_3N~x{emu%mtWTL>umeM0bRP~p_8!glw;Wz737+fDvhtV0nh5U zd(fseHN?89H*oE@({!*T|8V9?Be!VK2{$H9@DADO$9~WH3ue*N4D1!Fz;$w#7so+# zvnIc%t9lUZ#{eIXLP^PLMFhUcYGZsG=xw@JT$z)w#ZxrU5~}$*$;GB5#%Hj1PpD%y zj-wx-jZFy5U`J8Ce<+--5#HuAW-6oRzBNu3VNizr`ECJx_KAk>NB`_T92gs?iHP0l zw&H1^=noq@NB>)%7Pzwpl_zB5_TN`de4{>A}6=Q+)yuOlCR+{wv- z6WlEy7+T#G4&Gh_DnWyn*^$blQYks;xqdVw+tD|#`vCQ z$1oU5Iu=nuS9)noe$LDVTpl!^sAt*PaYv~W7BtrYyWdvj<;ms=6b}$g7_J9q93`@; zJl^8VPIyt1F~@_W;lrYtd(=g(xxK_h$m8=eQ`Ak@$CTJrg@bN9a0f@{HulJ>j^f;uvFPJ3^7IZy-7yLHyXZT+ za)Dx~C|HJbsu%LsIxjDglpI9HDTY_|f^u^AQgFAoL26HFh(09j7)OQj74@V>a$ob! z+VsT>KZObFayhc*9OIeFG;>{>;x5ZMeruwi^i%vp9H;hfflRF12Xdk=cg3~<+?7o( zd7Eeayd*a;!=1S!!F>zcgw(6V7&_5#P3|iD2*JFv(Hj=-XS=kXw+EZAUIQj3$%QRl zOk|YOq^$W(v>!y4;}jW7K2hk)dh};_sQBium^adtq4nMoi+6or*b)FLo6h^m(lie8 z0FdwaJ?aNu%D}oa_yPQI=M*Of=^Z%Jo z_J)|y3$u2%5$^KLd$#1o0iz+qQ!WhWY!Frk`rbro_&mqsWsRmgv@`*18A;Q(kN)MO cKH|ZH)bj@GyWpDZ2f&}4w6av0glWkC0Svm|6951J diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 6920ba3b2b..268fae5c27 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -43,6 +43,7 @@ import Data.Maybe (catMaybes, fromMaybe, listToMaybe, mapMaybe) import qualified Data.Set as S import qualified Data.Set as Set +import Data.String (IsString (..)) import Data.Text (Text) import qualified Data.Text as T import Data.Traversable (for) @@ -63,9 +64,9 @@ import Development.IDE (Action, WithPriority, catchSrcErrors, cmapWithPrio, define, - ideLogger, logPriority, - realSpan, use, - useWithStaleFast, + hieKind, ideLogger, + logPriority, realSpan, + use, useWithStaleFast, useWithStale_, uses) import Development.IDE.Core.Compile (TcModuleResult (..), lookupName) @@ -124,8 +125,7 @@ liftMaybeEither e = liftEither . maybe (Left e) Right -- computeSemanticTokens :: IdeState -> NormalizedFilePath -> Action (SemanticTokens) computeSemanticTokens :: p -> NormalizedFilePath -> ExceptT PluginError Action SemanticTokens computeSemanticTokens state nfp = do - (HAR {hieAst, refMap}, mapping) <- useWithStaleE GetHieAst nfp - typedAst :: HieASTs Type <- liftMaybeEither (PluginInternalError "HieAst a convert cast to HieASTs Type") $ cast hieAst + (HAR {..}, mapping) <- useWithStaleE GetHieAst nfp (TcModuleResult {..}, _) <- useWithStaleE TypeCheck nfp (GTTMap {importedNameSemanticMap}, _) <- useWithStaleE GetGlobalNameSemantic nfp (_, ast) <- @@ -133,11 +133,11 @@ computeSemanticTokens state nfp = do (PluginRuleFailed $ "hieAst does not contains ast for:" <> T.pack (show nfp)) $ listToMaybe $ Map.toList - $ getAsts typedAst + $ getAsts hieAst let nameSet = nameGetter tmrRenamed -- get current location from the old ones let spanNames = mapMaybe (\(span, name) -> (,name) <$> toCurrentRange mapping span) $ hieAstSpanNames nameSet ast - let sMap = plusNameEnv_C (<>) importedNameSemanticMap $ mkLocalNameSemanticFromAst ast refMap + let sMap = plusNameEnv_C (<>) importedNameSemanticMap $ mkLocalNameSemanticFromAst hieKind ast refMap let moduleAbsTks = extractSemanticTokensFromNames sMap spanNames withExceptT PluginInternalError $ liftEither $ semanticTokenAbsoluteSemanticTokens moduleAbsTks diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 75ceed0459..50169fe44a 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -5,6 +5,7 @@ This module contains the mapping 3. from hieAst identifier detail to our token type 4. from lsp token to our token -} +{-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} @@ -15,6 +16,7 @@ import qualified Data.Map as Map import Data.Maybe (fromMaybe, mapMaybe) import qualified Data.Set as Set import Data.Text (Text) +import Development.IDE (HieKind (HieFresh, HieFromDisk)) import Development.IDE.GHC.Compat import Ide.Plugin.SemanticTokens.Types import Language.LSP.Protocol.Types (LspEnum (knownValues), @@ -25,6 +27,8 @@ import Language.LSP.Protocol.Types (LspEnum (knownValues), UInt, absolutizeTokens) + + {- | 1. from our token type to LSP default token type -} @@ -65,23 +69,6 @@ fromLspTokenType tk = fromMaybe TNothing $ Map.lookup tk lspTokenReverseMap 2. from ghc type (TyThing or name) to our token type -} -typeSemantic :: Type -> SemanticTokenType -typeSemantic x = - case x of - ForAllTy _ a -> typeSemantic a - FunTy _ _ -> TFunction - _ -> TNothing - -- TyVarTy _ -> TNothing - -- TyConApp tyCon _ -> TNothing - -- -- | isTypeSynonymTyCon tyCon -> TTypeSyn - -- -- | isTypeFamilyTyCon tyCon -> TTypeFamily - -- -- | isClassTyCon tyCon -> TClass - -- -- | otherwise -> TTypeCon - -- AppTy a b -> TNothing - -- LitTy _ -> TNothing - -- CastTy _ _ -> TNothing - -- CoercionTy _ -> TNothing - toTokenType :: Name -> SemanticTokenType toTokenType locName = case occNameSpace $ occName locName of @@ -118,17 +105,34 @@ tyThingSemantic ty = case ty of ACoAxiom _ -> TNothing isFunVar :: Var -> Bool -isFunVar var = isFun $ varType var -isFun :: Type -> Bool -isFun a = case a of - ForAllTy _ a -> isFun a +isFunVar var = isFunType $ varType var +isFunType :: Type -> Bool +isFunType a = case a of + ForAllTy _ a -> isFunType a x -> isFunTy a {- | -3. from hieAst identifier detail to our token type +3. from HIE to our token type -} +typeSemantic :: HieKind hType -> hType -> SemanticTokenType +typeSemantic kind t = case kind of + HieFresh -> if isFunType t then TFunction else TNothing + HieFromDisk full_file -> + if isFixFunction (recoverFullType t (hie_types full_file)) + then TFunction + else TNothing + where + isFixFunction :: HieTypeFix -> Bool + isFixFunction (Roll x) = + case x of + HForAllTy _ a -> isFixFunction a + HFunTy {} -> True + _ -> False + + + infoTokenType :: ContextInfo -> SemanticTokenType infoTokenType x = case x of diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 05f82dc51e..aaf0d9cee2 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -30,7 +30,8 @@ import qualified Data.Text as Text import qualified Data.Text.Lazy.Builder as Text import Data.Tuple (swap) import Debug.Trace -import Development.IDE (Action, filePathToUri', +import Development.IDE (Action, HieKind, + filePathToUri', rangeToRealSrcSpan, realSpan, realSrcSpanToRange) @@ -75,11 +76,11 @@ nameGetter = everything unionNameSet (emptyNameSet `mkQ` nameToCollect) -------------------------------------------------- -mkLocalNameSemanticFromAst :: HieAST Type -> RefMap a -> NameSemanticMap -mkLocalNameSemanticFromAst ast rm = mkNameEnv (mapMaybe (nameNameSemanticFromHie ast rm) ns) +mkLocalNameSemanticFromAst :: HieKind a -> HieAST a -> RefMap a -> NameSemanticMap +mkLocalNameSemanticFromAst hieKind ast rm = mkNameEnv (mapMaybe (nameNameSemanticFromHie hieKind ast rm) ns) where ns = rights $ M.keys rm -nameNameSemanticFromHie :: HieAST Type -> RefMap a -> Name -> Maybe (Name,SemanticTokenType) -nameNameSemanticFromHie hie rm ns = do +nameNameSemanticFromHie :: forall a. HieKind a -> HieAST a -> RefMap a -> Name -> Maybe (Name,SemanticTokenType) +nameNameSemanticFromHie hieKind hie rm ns = do st <- -- traceShow ("to find Name", showName ns) $ nameSemanticFromRefMap rm ns return -- $ traceShow (showName ns, st) @@ -92,19 +93,19 @@ nameNameSemanticFromHie hie rm ns = do Map.lookup (Right name) rm let infos = S.unions $ map (identInfo . snd) spanInfos let bindTokenType = -- traceShow ("getDefinitionSite:infos", nameString, infos, mapMaybe getBindSiteFromContext $ S.toList infos) $ - listToMaybe (mapMaybe getBindSiteFromContext $ S.toList infos) >>= bindSiteMaybeTokenType + listToMaybe (mapMaybe getBindSiteFromContext $ S.toList infos) >>= bindSiteMaybeTokenType hieKind let contextInfoTokenType = contextInfosMaybeTokenType infos maximum <$> NE.nonEmpty (catMaybes [bindTokenType, contextInfoTokenType]) - spanTypeFromHie :: HieAST Type -> Span -> Maybe Type + spanTypeFromHie :: HieAST a -> Span -> Maybe a spanTypeFromHie ast span = do ast <- selectSmallestContaining span ast nodeInfo <- Map.lookup SourceInfo $ getSourcedNodeInfo $ sourcedNodeInfo ast -- usually for a visible name, the type is in the first child listToMaybe $ nodeType nodeInfo - bindSiteMaybeTokenType :: Span -> Maybe SemanticTokenType - bindSiteMaybeTokenType = fmap typeSemantic . spanTypeFromHie hie + bindSiteMaybeTokenType :: HieKind a -> Span -> Maybe SemanticTokenType + bindSiteMaybeTokenType hieKind = fmap (typeSemantic hieKind) . spanTypeFromHie hie contextInfosMaybeTokenType :: Set.Set ContextInfo -> Maybe SemanticTokenType contextInfosMaybeTokenType details = case NE.nonEmpty $ Set.toList details of From bfa4343751325d590ac1f83af29a0fb484dff94e Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 23 Dec 2023 11:55:37 +0800 Subject: [PATCH 43/73] add options to turn semantic tokens on and off --- hls-plugin-api/src/Ide/Plugin/Config.hs | 1 + hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs | 2 ++ hls-plugin-api/src/Ide/Types.hs | 10 +++++++--- .../src/Ide/Plugin/SemanticTokens.hs | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/hls-plugin-api/src/Ide/Plugin/Config.hs b/hls-plugin-api/src/Ide/Plugin/Config.hs index 785a7a5a92..81e5b7e1b1 100644 --- a/hls-plugin-api/src/Ide/Plugin/Config.hs +++ b/hls-plugin-api/src/Ide/Plugin/Config.hs @@ -66,6 +66,7 @@ parsePluginConfig :: PluginConfig -> Value -> A.Parser PluginConfig parsePluginConfig def = A.withObject "PluginConfig" $ \o -> PluginConfig <$> o .:? "globalOn" .!= plcGlobalOn def <*> o .:? "callHierarchyOn" .!= plcCallHierarchyOn def + <*> o .:? "semanticTokensOn" .!= plcSemanticTokensOn def <*> o .:? "codeActionsOn" .!= plcCodeActionsOn def <*> o .:? "codeLensOn" .!= plcCodeLensOn def <*> o .:? "diagnosticsOn" .!= plcDiagnosticsOn def -- AZ diff --git a/hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs b/hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs index 6111de4a48..da2751106c 100644 --- a/hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs +++ b/hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs @@ -93,6 +93,7 @@ pluginsToDefaultConfig IdePlugins {..} = SMethod_TextDocumentDocumentSymbol -> ["symbolsOn" A..= plcSymbolsOn] SMethod_TextDocumentCompletion -> ["completionOn" A..= plcCompletionOn] SMethod_TextDocumentPrepareCallHierarchy -> ["callHierarchyOn" A..= plcCallHierarchyOn] + SMethod_TextDocumentSemanticTokensFull -> ["semanticTokensOn" A..= plcSemanticTokensOn] _ -> [] -- | Generates json schema used in haskell vscode extension @@ -123,6 +124,7 @@ pluginsToVSCodeExtensionSchema IdePlugins {..} = A.object $ mconcat $ singlePlug SMethod_TextDocumentDocumentSymbol -> [toKey' "symbolsOn" A..= schemaEntry "symbols"] SMethod_TextDocumentCompletion -> [toKey' "completionOn" A..= schemaEntry "completions"] SMethod_TextDocumentPrepareCallHierarchy -> [toKey' "callHierarchyOn" A..= schemaEntry "call hierarchy"] + SMethod_TextDocumentSemanticTokensFull -> [toKey' "semanticTokensOn" A..= schemaEntry "semantic tokens"] _ -> [] schemaEntry desc = A.object diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index 709ff23bdd..1c4608e356 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -228,6 +228,7 @@ data PluginConfig = , plcRenameOn :: !Bool , plcSelectionRangeOn :: !Bool , plcFoldingRangeOn :: !Bool + , plcSemanticTokensOn :: !Bool , plcConfig :: !Object } deriving (Show,Eq) @@ -243,12 +244,13 @@ instance Default PluginConfig where , plcCompletionOn = True , plcRenameOn = True , plcSelectionRangeOn = True - , plcFoldingRangeOn = True + , plcFoldingRangeOn = True + , plcSemanticTokensOn = True , plcConfig = mempty } instance ToJSON PluginConfig where - toJSON (PluginConfig g ch ca cl d h s c rn sr fr cfg) = r + toJSON (PluginConfig g ch ca cl d h s c rn sr fr st cfg) = r where r = object [ "globalOn" .= g , "callHierarchyOn" .= ch @@ -261,6 +263,7 @@ instance ToJSON PluginConfig where , "renameOn" .= rn , "selectionRangeOn" .= sr , "foldingRangeOn" .= fr + , "semanticTokensOn" .= st , "config" .= cfg ] @@ -508,7 +511,8 @@ instance PluginMethod Request Method_TextDocumentFormatting where pid = pluginId pluginDesc instance PluginMethod Request Method_TextDocumentSemanticTokensFull where - pluginEnabled _ msgParams pluginDesc _ = pluginResponsible uri pluginDesc + pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc + && pluginEnabledConfig plcSemanticTokensOn (configForPlugin conf pluginDesc) where uri = msgParams ^. L.textDocument . L.uri diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index 66a3ec33c6..7ca6b8a5c4 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -11,7 +11,7 @@ import Language.LSP.Protocol.Message descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState -descriptor recorder plId = (defaultPluginDescriptor plId "provides lsp semantic tokens features") +descriptor recorder plId = (defaultPluginDescriptor plId "Provides semantic tokens") { Ide.Types.pluginHandlers = mkPluginHandler SMethod_TextDocumentSemanticTokensFull Internal.semanticTokensFull , Ide.Types.pluginRules = From 0fe815e5f3a6f5df292073e846c590003f73f4ef Mon Sep 17 00:00:00 2001 From: fendor Date: Sat, 23 Dec 2023 12:32:51 +0100 Subject: [PATCH 44/73] Disable stan plugin by default (#3917) --- plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs | 9 ++++++++- plugins/hls-stan-plugin/test/Main.hs | 16 +++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs index f45a604a67..63e4de376d 100644 --- a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs +++ b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs @@ -32,6 +32,7 @@ import GHC.Generics (Generic) import Ide.Plugin.Config import Ide.Types (PluginDescriptor (..), PluginId, configHasDiagnostics, + configInitialGenericConfig, defaultConfigDescriptor, defaultPluginDescriptor, pluginEnabledConfig) @@ -46,11 +47,17 @@ import Stan.Observation (Observation (..)) descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState descriptor recorder plId = (defaultPluginDescriptor plId desc) { pluginRules = rules recorder plId - , pluginConfigDescriptor = defaultConfigDescriptor + , pluginConfigDescriptor = defConfigDescriptor { configHasDiagnostics = True + -- We disable this plugin by default because users have been complaining about + -- the diagnostics, see https://github.com/haskell/haskell-language-server/issues/3916 + , configInitialGenericConfig = (configInitialGenericConfig defConfigDescriptor) + { plcGlobalOn = False + } } } where + defConfigDescriptor = defaultConfigDescriptor desc = "Provides stan diagnostics. Built with stan-" <> VERSION_stan newtype Log = LogShake Shake.Log deriving (Show) diff --git a/plugins/hls-stan-plugin/test/Main.hs b/plugins/hls-stan-plugin/test/Main.hs index 6c27e399d3..81d23ec928 100644 --- a/plugins/hls-stan-plugin/test/Main.hs +++ b/plugins/hls-stan-plugin/test/Main.hs @@ -10,6 +10,7 @@ import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.IO as T import qualified Ide.Plugin.Stan as Stan +import Ide.Types import qualified Language.LSP.Protocol.Lens as L import System.FilePath import Test.Hls @@ -39,7 +40,20 @@ testDir :: FilePath testDir = "test/testdata" stanPlugin :: PluginTestDescriptor Stan.Log -stanPlugin = mkPluginTestDescriptor Stan.descriptor "stan" +stanPlugin = mkPluginTestDescriptor enabledStanDescriptor "stan" + where + -- We have to explicitly enable the plugin as it is disabled by default as + -- per request: https://github.com/haskell/haskell-language-server/issues/3916 + -- + enabledStanDescriptor recorder plId = + let stanPluginDescriptor = Stan.descriptor recorder plId + in stanPluginDescriptor + { pluginConfigDescriptor = (pluginConfigDescriptor stanPluginDescriptor) + { configInitialGenericConfig = (configInitialGenericConfig (pluginConfigDescriptor stanPluginDescriptor)) + { plcGlobalOn = True + } + } + } runStanSession :: FilePath -> Session a -> IO a runStanSession subdir = From f96cdfb04de0650873990d29fe0f059dde2273a2 Mon Sep 17 00:00:00 2001 From: Patrick Wales Date: Tue, 26 Dec 2023 17:03:58 +0800 Subject: [PATCH 45/73] Fix positionMapping in stale data (#3920) * Fix positionMapping in stale data * add test for updatePositionMapping * add comment to demonstrate addOldDelta --- ghcide/ghcide.cabal | 1 + .../Development/IDE/Core/PositionMapping.hs | 12 ++++--- ghcide/src/Development/IDE/Core/Shake.hs | 31 ++++++++++++------- ghcide/test/exe/PositionMappingTests.hs | 27 ++++++++++++++-- 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index 2a5854d16a..72423db76b 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -336,6 +336,7 @@ test-suite ghcide-tests , containers , data-default , directory + , enummapset , extra , filepath , fuzzy diff --git a/ghcide/src/Development/IDE/Core/PositionMapping.hs b/ghcide/src/Development/IDE/Core/PositionMapping.hs index 82d8334c87..d04856389c 100644 --- a/ghcide/src/Development/IDE/Core/PositionMapping.hs +++ b/ghcide/src/Development/IDE/Core/PositionMapping.hs @@ -10,7 +10,7 @@ module Development.IDE.Core.PositionMapping , fromCurrentPosition , toCurrentPosition , PositionDelta(..) - , addDelta + , addOldDelta , idDelta , composeDelta , mkDelta @@ -119,9 +119,13 @@ idDelta = PositionDelta pure pure mkDelta :: [TextDocumentContentChangeEvent] -> PositionDelta mkDelta cs = foldl' applyChange idDelta cs --- | Add a new delta onto a Mapping k n to make a Mapping (k - 1) n -addDelta :: PositionDelta -> PositionMapping -> PositionMapping -addDelta delta (PositionMapping pm) = PositionMapping (composeDelta delta pm) +-- | addOldDelta +-- Add a old delta onto a Mapping k n to make a Mapping (k - 1) n +addOldDelta :: + PositionDelta -- ^ delta from version k - 1 to version k + -> PositionMapping -- ^ The input mapping is from version k to version n + -> PositionMapping -- ^ The output mapping is from version k - 1 to version n +addOldDelta delta (PositionMapping pm) = PositionMapping (composeDelta pm delta) -- TODO: We currently ignore the right hand side (if there is only text), as -- that was what was done with lsp* 1.6 packages diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index 80837a6668..fbe1ab1b8a 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -62,6 +62,7 @@ module Development.IDE.Core.Shake( FileVersion(..), Priority(..), updatePositionMapping, + updatePositionMappingHelper, deleteValue, recordDirtyKeys, WithProgressFunc, WithIndefiniteProgressFunc, ProgressEvent(..), @@ -266,7 +267,7 @@ data ShakeExtras = ShakeExtras -- ^ Map from a text document version to a PositionMapping that describes how to map -- positions in a version of that document to positions in the latest version -- First mapping is delta from previous version and second one is an - -- accumulation of all previous mappings. + -- accumulation to the current version. ,progress :: ProgressReporting ,ideTesting :: IdeTesting -- ^ Whether to enable additional lsp messages used by the test suite for checking invariants @@ -443,7 +444,7 @@ lastValueIO s@ShakeExtras{positionMapping,persistentKeys,state} k file = do `catch` (\(_ :: IOException) -> pure Nothing) atomicallyNamed "lastValueIO 2" $ do STM.focus (Focus.alter (alterValue $ Stale (Just del) actual_version (toDyn v))) (toKey k file) state - Just . (v,) . addDelta del <$> mappingForVersion positionMapping file actual_version + Just . (v,) . addOldDelta del <$> mappingForVersion positionMapping file actual_version -- We got a new stale value from the persistent rule, insert it in the map without affecting diagnostics alterValue new Nothing = Just (ValueWithDiagnostics new mempty) -- If it wasn't in the map, give it empty diagnostics @@ -459,7 +460,7 @@ lastValueIO s@ShakeExtras{positionMapping,persistentKeys,state} k file = do Succeeded ver (fromDynamic -> Just v) -> atomicallyNamed "lastValueIO 5" $ Just . (v,) <$> mappingForVersion positionMapping file ver Stale del ver (fromDynamic -> Just v) -> - atomicallyNamed "lastValueIO 6" $ Just . (v,) . maybe id addDelta del <$> mappingForVersion positionMapping file ver + atomicallyNamed "lastValueIO 6" $ Just . (v,) . maybe id addOldDelta del <$> mappingForVersion positionMapping file ver Failed p | not p -> readPersistent _ -> pure Nothing @@ -1352,12 +1353,18 @@ updatePositionMapping IdeState{shakeExtras = ShakeExtras{positionMapping}} Versi STM.focus (Focus.alter f) uri positionMapping where uri = toNormalizedUri _uri - f = Just . f' . fromMaybe mempty - f' mappingForUri = snd $ - -- Very important to use mapAccum here so that the tails of - -- each mapping can be shared, otherwise quadratic space is - -- used which is evident in long running sessions. - EM.mapAccumRWithKey (\acc _k (delta, _) -> let new = addDelta delta acc in (new, (delta, acc))) - zeroMapping - (EM.insert _version (shared_change, zeroMapping) mappingForUri) - shared_change = mkDelta changes + f = Just . updatePositionMappingHelper _version changes . fromMaybe mempty + + +updatePositionMappingHelper :: + Int32 + -> [TextDocumentContentChangeEvent] + -> EnumMap Int32 (PositionDelta, PositionMapping) + -> EnumMap Int32 (PositionDelta, PositionMapping) +updatePositionMappingHelper ver changes mappingForUri = snd $ + -- Very important to use mapAccum here so that the tails of + -- each mapping can be shared, otherwise quadratic space is + -- used which is evident in long running sessions. + EM.mapAccumRWithKey (\acc _k (delta, _) -> let new = addOldDelta delta acc in (new, (delta, acc))) + zeroMapping + (EM.insert ver (mkDelta changes, zeroMapping) mappingForUri) diff --git a/ghcide/test/exe/PositionMappingTests.hs b/ghcide/test/exe/PositionMappingTests.hs index 083e765db0..8ffbdfd4c1 100644 --- a/ghcide/test/exe/PositionMappingTests.hs +++ b/ghcide/test/exe/PositionMappingTests.hs @@ -3,6 +3,7 @@ module PositionMappingTests (tests) where +import qualified Data.EnumMap.Strict as EM import Data.Row import qualified Data.Text as T import Data.Text.Utf16.Rope (Rope) @@ -10,7 +11,8 @@ import qualified Data.Text.Utf16.Rope as Rope import Development.IDE.Core.PositionMapping (PositionResult (..), fromCurrent, positionResultToMaybe, - toCurrent) + toCurrent, + toCurrentPosition) import Development.IDE.Types.Location import Language.LSP.Protocol.Types hiding (SemanticTokenAbsolute (..), @@ -20,15 +22,36 @@ import Language.LSP.Protocol.Types hiding import Language.LSP.VFS (applyChange) import Test.QuickCheck -- import Test.QuickCheck.Instances () +import Control.Arrow (second) import Data.Functor.Identity (runIdentity) +import Data.Text (Text) +import Development.IDE.Core.Shake (updatePositionMappingHelper) import Test.Tasty import Test.Tasty.HUnit import Test.Tasty.QuickCheck +enumMapMappingTest :: TestTree +enumMapMappingTest = testCase "enumMapMappingTest" $ do + let mkChangeEvent :: Range -> Text -> TextDocumentContentChangeEvent + mkChangeEvent r t = TextDocumentContentChangeEvent $ InL $ #range .== r .+ #rangeLength .== Nothing .+ #text .== t + mkCE :: UInt -> UInt -> UInt -> UInt -> Text -> TextDocumentContentChangeEvent + mkCE l1 c1 l2 c2 = mkChangeEvent (Range (Position l1 c1) (Position l2 c2)) + events :: [(Int32, [TextDocumentContentChangeEvent])] + events = map (second return) [(0, mkCE 0 0 0 0 ""), (1, mkCE 0 1 0 1 " "), (2, mkCE 0 2 0 2 " "), (3, mkCE 0 3 0 3 " "), (4, mkCE 0 4 0 4 " "), (5, mkCE 0 5 0 5 " ")] + finalMap = Prelude.foldl (\m (i, e) -> updatePositionMappingHelper i e m) mempty events + let updatePose fromPos = do + mapping <- snd <$> EM.lookup 0 finalMap + toCurrentPosition mapping fromPos + updatePose (Position 0 4) @?= Just (Position 0 9) + updatePose (Position 0 5) @?= Just (Position 0 10) + + tests :: TestTree tests = testGroup "position mapping" - [ testGroup "toCurrent" + [ + enumMapMappingTest + , testGroup "toCurrent" [ testCase "before" $ toCurrent (Range (Position 0 1) (Position 0 3)) From a08655f096cac851bb85497cb463c5180e278779 Mon Sep 17 00:00:00 2001 From: Patrick Date: Wed, 27 Dec 2023 11:55:29 +0800 Subject: [PATCH 46/73] cleanup --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 6 +----- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 1 - .../src/Ide/Plugin/SemanticTokens/Query.hs | 6 ------ .../src/Ide/Plugin/SemanticTokens/Types.hs | 8 +++----- .../src/Ide/Plugin/SemanticTokens/Utils.hs | 6 ------ 5 files changed, 4 insertions(+), 23 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 268fae5c27..ccaf17798d 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -143,9 +143,7 @@ computeSemanticTokens state nfp = do semanticTokensFull :: PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull semanticTokensFull state _ param = do - -- let dbg = logWith state nfp <- getNormalizedFilePathE (param ^. L.textDocument . L.uri) --- logWith state Info $ "computeSemanticTokens: " <> show nfp items <- runActionE "SemanticTokens.semanticTokensFull" state $ computeSemanticTokens state nfp return $ InL items @@ -154,7 +152,6 @@ getImportedNameSemanticRule recorder = define (cmapWithPrio LogShake recorder) $ \GetGlobalNameSemantic nfp -> do (TcModuleResult {..}, _) <- useWithStale_ TypeCheck nfp (hscEnv -> hsc, _) <- useWithStale_ GhcSessionDeps nfp - (HAR {hieAst, refMap}, _) <- useWithStale_ GetHieAst nfp let nameSet = nameGetter tmrRenamed tm <- liftIO $ foldrM (getTypeExclude (tcg_type_env tmrTypechecked) hsc) emptyNameEnv $ nameSetElemsStable nameSet return ([], Just $ GTTMap tm) @@ -162,8 +159,7 @@ getImportedNameSemanticRule recorder = -- ignore one already in current module getTypeExclude localMap env n nameMap | Nothing <- lookupNameEnv localMap n = - do - tyThing <- lookupImported env n + do tyThing <- lookupImported env n pure $ maybe nameMap (extendNameEnv nameMap n . tyThingSemantic) tyThing | otherwise = pure nameMap lookupImported :: HscEnv -> Name -> IO (Maybe TyThing) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 50169fe44a..a0acbe9eb7 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -181,7 +181,6 @@ recoverSemanticTokens sourceCode (SemanticTokens _ xs) = fmap (tokenOrigin sourc where tLine = lines sourceCode !? fromIntegral line name = maybe "no source" (take (fromIntegral len) . drop (fromIntegral startChar)) tLine - dataActualToken :: [UInt] -> Either Text [ActualToken] dataActualToken xs = maybe decodeError (Right . fmap semanticTokenAbsoluteActualToken . absolutizeTokens) $ mapM fromTuple (chunksOf 5 $ map fromIntegral xs) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index aaf0d9cee2..7810a5d83a 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -138,7 +138,6 @@ hieAstSpanNames nameSet ast = if null (nodeChildren ast) then combineNodeIds :: Map.Map Identifier (IdentifierDetails a) -> NodeInfo a -> Map.Map Identifier (IdentifierDetails a) - -- ad `combineNodeIds` (NodeInfo SourceInfo _ bd) = bd ad `combineNodeIds` (NodeInfo _ _ bd) = Map.unionWith (<>) ad bd @@ -163,12 +162,7 @@ extractSemanticTokensFromNames nsm = let len = endColumn - startColumn in SemanticTokenAbsolute (fromIntegral startLine) (fromIntegral startColumn) (fromIntegral len) <$> toLspTokenType tokenType <*> return [] - -- SemanticTokenModifiers_Declaration - - -- getSemantic :: NameSemanticMap -> (Span, Name) -> Maybe (Span, SemanticTokenType) getSemantic nameMap (span, name) = do - -- let tkt = toTokenType name - -- let tokenType = maybe tkt (\x -> tkt <> x) $ Map.lookup name nameMap let tokenType = lookupNameEnv nameMap name fmap (span,) tokenType diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 9272f7cc68..9958d25183 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -27,17 +27,15 @@ import Language.LSP.Protocol.Types -- and we might want to merge them. data SemanticTokenType = TNothing -- unknown - -- | TVariable -- fallback - -- since many thing can be valbind. we put it as less priority - | TVariable -- valBind instance bind or regular bind + | TVariable -- none function variable | TFunction -- function | TDataCon -- Data constructor | TTypeVariable -- Type variable | TClassMethod -- Class method | TPatternSyn -- Pattern synonym | TTypeCon -- Type (Type constructor) - | TClass -- Class (ConstraUInt constructor) - | TTypeSyn -- Type synonym (Non-local is not captured) + | TClass -- Type class + | TTypeSyn -- Type synonym | TTypeFamily -- type family | TRecField -- from match bind deriving (Eq, Ord, Show, Enum, Bounded) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs index 90a967d527..8917959e6a 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs @@ -90,9 +90,3 @@ nameMapString nsm names = unlines showSpan :: RealSrcSpan -> String showSpan x = show (srcSpanStartLine x) <> ":" <> show (srcSpanStartCol x) <> "-" <> show (srcSpanEndCol x) - - - -type family Foo a where - Foo Int = Int - Foo a = String From 918cf8384b8e8fb867da12a0a6c08fdf446ca7ab Mon Sep 17 00:00:00 2001 From: Patrick Date: Wed, 27 Dec 2023 16:08:05 +0800 Subject: [PATCH 47/73] fix: for local variable, extract type from contextInfo instead of bind site, thus function in pattern binds can also be indentified --- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 5 ++++- .../src/Ide/Plugin/SemanticTokens/Query.hs | 15 ++------------- .../test/testdata/TPatternbind.expected | 7 +++---- .../test/testdata/TPatternbind.hs | 2 +- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index a0acbe9eb7..26304ec46f 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -120,9 +120,12 @@ typeSemantic :: HieKind hType -> hType -> SemanticTokenType typeSemantic kind t = case kind of HieFresh -> if isFunType t then TFunction else TNothing HieFromDisk full_file -> - if isFixFunction (recoverFullType t (hie_types full_file)) + if isFixFunction fullType then TFunction else TNothing + where + fullType = recoverFullType t (hie_types full_file) + where isFixFunction :: HieTypeFix -> Bool isFixFunction (Roll x) = diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 7810a5d83a..04d1011851 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -92,20 +92,9 @@ nameNameSemanticFromHie hieKind hie rm ns = do spanInfos <- -- traceShow ("getting spans:", nameString) $ Map.lookup (Right name) rm let infos = S.unions $ map (identInfo . snd) spanInfos - let bindTokenType = -- traceShow ("getDefinitionSite:infos", nameString, infos, mapMaybe getBindSiteFromContext $ S.toList infos) $ - listToMaybe (mapMaybe getBindSiteFromContext $ S.toList infos) >>= bindSiteMaybeTokenType hieKind + let typeTokenType = fmap (typeSemantic hieKind) $ listToMaybe $ mapMaybe (identType . snd) spanInfos let contextInfoTokenType = contextInfosMaybeTokenType infos - maximum <$> NE.nonEmpty (catMaybes [bindTokenType, contextInfoTokenType]) - - spanTypeFromHie :: HieAST a -> Span -> Maybe a - spanTypeFromHie ast span = do - ast <- selectSmallestContaining span ast - nodeInfo <- Map.lookup SourceInfo $ getSourcedNodeInfo $ sourcedNodeInfo ast - -- usually for a visible name, the type is in the first child - listToMaybe $ nodeType nodeInfo - - bindSiteMaybeTokenType :: HieKind a -> Span -> Maybe SemanticTokenType - bindSiteMaybeTokenType hieKind = fmap (typeSemantic hieKind) . spanTypeFromHie hie + maximum <$> NE.nonEmpty (catMaybes [typeTokenType, contextInfoTokenType]) contextInfosMaybeTokenType :: Set.Set ContextInfo -> Maybe SemanticTokenType contextInfosMaybeTokenType details = case NE.nonEmpty $ Set.toList details of diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected index 2fa8c1752c..098267a315 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected @@ -1,8 +1,7 @@ SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 3, startChar = 2, len = 1}, name = "a"} SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 3, startChar = 5, len = 1}, name = "b"} SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 5, startChar = 1, len = 1}, name = "f"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 3, len = 1}, name = "x"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 5, startChar = 3, len = 1}, name = "g"} SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 5, len = 1}, name = "y"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 9, len = 1}, name = "x"} -SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 11, len = 1}, name = "+"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 13, len = 1}, name = "y"} +SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 5, startChar = 9, len = 1}, name = "g"} +SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 11, len = 1}, name = "y"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.hs index 6cfe5348a7..49e642a35d 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.hs @@ -2,7 +2,7 @@ module TVariable where (a, b) = (1, 2) -f x y = x + y +f g y = g y From 159eb89b3cf580694284ff66827608e710dcea04 Mon Sep 17 00:00:00 2001 From: Patrick Date: Wed, 27 Dec 2023 21:58:03 +0800 Subject: [PATCH 48/73] clean up --- .../hls-semantic-tokens-plugin.cabal | 1 + .../src/Ide/Plugin/SemanticTokens.hs | 1 - .../src/Ide/Plugin/SemanticTokens/Internal.hs | 118 +++----- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 268 ++++++++---------- .../src/Ide/Plugin/SemanticTokens/Query.hs | 105 +++---- .../src/Ide/Plugin/SemanticTokens/Types.hs | 28 +- .../src/Ide/Plugin/SemanticTokens/Utils.hs | 6 +- .../hls-semantic-tokens-plugin/test/Main.hs | 25 +- .../test/testdata/T1.expected | 162 +++++------ .../test/testdata/TClass.expected | 10 +- .../testdata/TClassImportedDeriving.expected | 6 +- .../test/testdata/TDataFamily.expected | 24 +- .../test/testdata/TDataType.expected | 8 +- .../test/testdata/TDatatypeImported.expected | 8 +- .../test/testdata/TFunction.expected | 22 +- .../test/testdata/TFunctionLet.expected | 10 +- .../test/testdata/TFunctionLocal.expected | 14 +- .../test/testdata/TGADT.expected | 26 +- .../TInstanceClassMethodBind.expected | 14 +- .../testdata/TInstanceClassMethodUse.expected | 4 +- .../test/testdata/TPatternMatch.expected | 4 +- .../test/testdata/TPatternSyn.expected | 2 +- .../test/testdata/TPatternbind.expected | 14 +- .../test/testdata/TRecord.expected | 8 +- .../test/testdata/TTypefamily.expected | 16 +- .../test/testdata/TValBind.expected | 8 +- 26 files changed, 412 insertions(+), 500 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal index da6bfe4bbb..488de75320 100644 --- a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal +++ b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal @@ -20,6 +20,7 @@ source-repository head location: https://github.com/haskell/haskell-language-server.git library + ghc-options: -Wall buildable: True exposed-modules: Ide.Plugin.SemanticTokens diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index 7ca6b8a5c4..d77d1ba28a 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -4,7 +4,6 @@ module Ide.Plugin.SemanticTokens (descriptor) where import Development.IDE import Development.IDE.Core.Rules (Log) import qualified Ide.Plugin.SemanticTokens.Internal as Internal -import qualified Ide.Plugin.SemanticTokens.Types as Types import Ide.Types import Language.LSP.Protocol.Message diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index ccaf17798d..c4573f0bc1 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -1,3 +1,9 @@ +----------------------------------------------------------------------------- +-- | +-- This module provides the core functionality of the plugin. +-- +----------------------------------------------------------------------------- + {-# LANGUAGE DataKinds #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleInstances #-} @@ -10,101 +16,50 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} -module Ide.Plugin.SemanticTokens.Internal where - --- import Language.LSP.Protocol.Types.Common +module Ide.Plugin.SemanticTokens.Internal (semanticTokensFull, getImportedNameSemanticRule, persistentSemanticMapRule) where --- import System.FilePath (takeExtension) -import Control.Arrow ((&&&), (+++)) import Control.Lens ((^.)) -import Control.Monad (forM, forM_) -import Control.Monad.Except (MonadError (..), +import Control.Monad.Except (ExceptT, MonadError (..), liftEither, withExceptT) import Control.Monad.IO.Class (MonadIO, liftIO) -import Control.Monad.Trans.Class (lift) -import Control.Monad.Trans.Except (ExceptT (ExceptT), - runExceptT) -import Control.Monad.Trans.Maybe (MaybeT (..)) -import Data.ByteString (ByteString, unpack) -import Data.Data (Data) -import Data.Either (fromRight, rights) -import Data.Either.Extra (lefts) -import Data.Function (on) -import Data.Generics (Generic, Typeable, - everything, mkQ) -import qualified Data.HashSet as HashSet -import Data.List (sortBy) -import qualified Data.List as List -import Data.List.Extra (chunksOf, (!?)) -import qualified Data.List.NonEmpty as NonEmpty -import qualified Data.Map as M +import Data.Either (fromRight) import qualified Data.Map as Map -import Data.Maybe (catMaybes, fromMaybe, - listToMaybe, mapMaybe) -import qualified Data.Set as S -import qualified Data.Set as Set -import Data.String (IsString (..)) -import Data.Text (Text) +import Data.Maybe (listToMaybe, mapMaybe) import qualified Data.Text as T -import Data.Traversable (for) -import Data.Typeable (cast) -import Debug.Trace (trace) import Development.IDE (Action, - DocAndKindMap (DKMap), - GetBindings (GetBindings), - GetDocMap (GetDocMap), GetHieAst (GetHieAst), - GetModIface (GetModIface), - GhcSessionDeps (GhcSessionDeps, GhcSessionDeps_), + GhcSessionDeps (GhcSessionDeps), HieAstResult (HAR, hieAst, hieModule, refMap), - IdeAction, IdeState, - Priority (..), Recorder, - RuleResult, Rules, + IdeState, Priority (..), + Recorder, Rules, TypeCheck (TypeCheck), WithPriority, catchSrcErrors, cmapWithPrio, define, hieKind, ideLogger, - logPriority, realSpan, - use, useWithStaleFast, - useWithStale_, uses) + logPriority, + useWithStale_) import Development.IDE.Core.Compile (TcModuleResult (..), lookupName) import Development.IDE.Core.PluginUtils (runActionE, - toCurrentPositionMT, - useMT, useWithStaleE, - useWithStaleFastMT, - useWithStaleMT, usesMT) -import Development.IDE.Core.PositionMapping (fromCurrentRange, - idDelta, - toCurrentPosition, - toCurrentRange, - zeroMapping) -import Development.IDE.Core.Rules (Log (LogShake), - getSourceFileSource, - runAction) -import Development.IDE.Core.Shake (ShakeExtras (..), - addPersistentRule, - getShakeExtras, - withHieDb) + useWithStaleE) +import Development.IDE.Core.PositionMapping (idDelta, toCurrentRange) +import Development.IDE.Core.Rules (Log (LogShake)) +import Development.IDE.Core.Shake (addPersistentRule) import Development.IDE.GHC.Compat -import Development.IDE.Graph.Classes (Hashable, NFData (rnf)) -import Development.IDE.Spans.Documentation (mkDocMap) -import Development.IDE.Spans.LocalBindings (getDefiningBindings, - getLocalScope) -import Development.IDE.Types.Exports (ExportsMap (..), - createExportsMapHieDb) import Development.IDE.Types.HscEnvEq (hscEnv) import Ide.Plugin.Error (PluginError (PluginInternalError, PluginRuleFailed), getNormalizedFilePathE) import Ide.Plugin.SemanticTokens.Mappings import Ide.Plugin.SemanticTokens.Query import Ide.Plugin.SemanticTokens.Types -import Ide.Plugin.SemanticTokens.Utils import Ide.Types import qualified Language.LSP.Protocol.Lens as L -import Language.LSP.Protocol.Message -import Language.LSP.Protocol.Types +import Language.LSP.Protocol.Message (Method (Method_TextDocumentSemanticTokensFull)) +import Language.LSP.Protocol.Types (NormalizedFilePath, + SemanticTokens, + type (|?) (InL)) +import Prelude hiding (span) logWith :: (MonadIO m) => IdeState -> Priority -> String -> m () logWith st prior = liftIO . logPriority (ideLogger st) prior . T.pack @@ -116,15 +71,21 @@ liftMaybeEither e = liftEither . maybe (Left e) Right ---- the api ----------------------- --- | computeSemanticTokens --- collect from various source --- imported name token type from GetGlobalNameSemantic --- local names tokenType from hieAst --- name locations from hieAst --- visible names from renamedSource --- computeSemanticTokens :: IdeState -> NormalizedFilePath -> Action (SemanticTokens) -computeSemanticTokens :: p -> NormalizedFilePath -> ExceptT PluginError Action SemanticTokens +-- | Compute semantic tokens for a Haskell source file. +-- +-- This function collects information from various sources, including: +-- +-- Imported name token type from Rule 'GetGlobalNameSemantic' +-- Local names token type from 'hieAst' +-- Name locations from 'hieAst' +-- Visible names from 'tmrRenamed' +-- +-- It then combines this information to compute the semantic tokens for the file. +-- +computeSemanticTokens :: IdeState -> NormalizedFilePath -> ExceptT PluginError Action SemanticTokens computeSemanticTokens state nfp = do + let dbg = logWith state Debug + dbg $ "Computing semantic tokens for: " <> show nfp (HAR {..}, mapping) <- useWithStaleE GetHieAst nfp (TcModuleResult {..}, _) <- useWithStaleE TypeCheck nfp (GTTMap {importedNameSemanticMap}, _) <- useWithStaleE GetGlobalNameSemantic nfp @@ -137,7 +98,7 @@ computeSemanticTokens state nfp = do let nameSet = nameGetter tmrRenamed -- get current location from the old ones let spanNames = mapMaybe (\(span, name) -> (,name) <$> toCurrentRange mapping span) $ hieAstSpanNames nameSet ast - let sMap = plusNameEnv_C (<>) importedNameSemanticMap $ mkLocalNameSemanticFromAst hieKind ast refMap + let sMap = plusNameEnv_C (<>) importedNameSemanticMap $ mkLocalNameSemanticFromAst hieKind refMap let moduleAbsTks = extractSemanticTokensFromNames sMap spanNames withExceptT PluginInternalError $ liftEither $ semanticTokenAbsoluteSemanticTokens moduleAbsTks @@ -147,6 +108,7 @@ semanticTokensFull state _ param = do items <- runActionE "SemanticTokens.semanticTokensFull" state $ computeSemanticTokens state nfp return $ InL items +-- | Defines the 'getImportedNameSemanticRule' function, which is used to record semantic tokens for imported names. getImportedNameSemanticRule :: Recorder (WithPriority Log) -> Rules () getImportedNameSemanticRule recorder = define (cmapWithPrio LogShake recorder) $ \GetGlobalNameSemantic nfp -> do diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 26304ec46f..9859bee497 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -1,16 +1,17 @@ -{- | Mappings.hs -This module contains the mapping -1. our token type from(to) LSP default token type -2. from ghc type to our token type -3. from hieAst identifier detail to our token type -4. from lsp token to our token --} {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} - +{-# LANGUAGE TypeFamilies #-} + +-- | +-- This module provides mappings to convert token type information in the Haskell IDE plugin. It includes functions for: +-- +-- 1. Mapping semantic token type to and from the LSP default token type. +-- 2. Mapping from GHC type and tyThing to semantic token type. +-- 3. Mapping from hieAst identifier details to haskell semantic token type. +-- 4. Mapping from LSP tokens to SemanticTokenOriginal. module Ide.Plugin.SemanticTokens.Mappings where -import qualified Data.List as List + import Data.List.Extra (chunksOf, (!?)) import qualified Data.Map as Map import Data.Maybe (fromMaybe, mapMaybe) @@ -26,49 +27,39 @@ import Language.LSP.Protocol.Types (LspEnum (knownValues), SemanticTokens (SemanticTokens), UInt, absolutizeTokens) +-- * 1. Mapping semantic token type to and from the LSP default token type. - - -{- | -1. from our token type to LSP default token type --} - - --- mapping from our token type to LSP default token type +-- | map from haskell semantic token type to LSP default token type toLspTokenType :: SemanticTokenType -> Maybe SemanticTokenTypes toLspTokenType tk = case tk of - -- TVariable -> SemanticTokenTypes_Variable - -- left hand side of none pattern bind - TFunction -> Just SemanticTokenTypes_Function - TVariable -> Just SemanticTokenTypes_Variable - TClass -> Just SemanticTokenTypes_Class - TClassMethod -> Just SemanticTokenTypes_Method - TTypeVariable -> Just SemanticTokenTypes_TypeParameter - -- normal data type is a tagged union type look like enum type - -- and a record is a product type like struct - -- but we don't distinguish them yet - TTypeCon -> Just SemanticTokenTypes_Enum - TDataCon -> Just SemanticTokenTypes_EnumMember - TRecField -> Just SemanticTokenTypes_Property - -- pattern syn is like a limited version of macro of constructing a data type - TPatternSyn -> Just SemanticTokenTypes_Macro - -- saturated type - TTypeSyn -> Just SemanticTokenTypes_Type - -- not sure if this is correct choice - TTypeFamily -> Just SemanticTokenTypes_Interface - TNothing -> Nothing + -- TVariable -> SemanticTokenTypes_Variable + -- left hand side of none pattern bind + TFunction -> Just SemanticTokenTypes_Function + TVariable -> Just SemanticTokenTypes_Variable + TClass -> Just SemanticTokenTypes_Class + TClassMethod -> Just SemanticTokenTypes_Method + TTypeVariable -> Just SemanticTokenTypes_TypeParameter + -- normal data type is a tagged union type look like enum type + -- and a record is a product type like struct + -- but we don't distinguish them yet + TTypeCon -> Just SemanticTokenTypes_Enum + TDataCon -> Just SemanticTokenTypes_EnumMember + TRecField -> Just SemanticTokenTypes_Property + -- pattern syn is like a limited version of macro of constructing a data type + TPatternSyn -> Just SemanticTokenTypes_Macro + -- saturated type + TTypeSyn -> Just SemanticTokenTypes_Type + -- not sure if this is correct choice + TTypeFamily -> Just SemanticTokenTypes_Interface + TNothing -> Nothing lspTokenReverseMap :: Map.Map SemanticTokenTypes SemanticTokenType -lspTokenReverseMap = Map.fromList $ mapMaybe (\x -> fmap (,x) (toLspTokenType x) ) $ enumFrom minBound +lspTokenReverseMap = Map.fromList $ mapMaybe (\x -> fmap (,x) (toLspTokenType x)) $ enumFrom minBound fromLspTokenType :: SemanticTokenTypes -> SemanticTokenType fromLspTokenType tk = fromMaybe TNothing $ Map.lookup tk lspTokenReverseMap - -{- | -2. from ghc type (TyThing or name) to our token type --} - +-- * 2. Mapping from GHC type and tyThing to semantic token type. toTokenType :: Name -> SemanticTokenType toTokenType locName = case occNameSpace $ occName locName of @@ -78,124 +69,111 @@ toTokenType locName = case occNameSpace $ occName locName of x | isVarNameSpace x -> TVariable _ -> TNothing - -- | tyThingSemantic --- from ghc source code https://hackage.haskell.org/package/ghc-9.6.3/docs/src/GHC.Core.TyCon.html#isDataTyCon tyThingSemantic :: TyThing -> SemanticTokenType tyThingSemantic ty = case ty of - AnId vid - | isTyVar vid -> TTypeVariable - | isRecordSelector vid -> TRecField - | isClassOpId vid -> TClassMethod - | isFunVar vid -> TFunction - -- | isDFunId vid -> TClassMethod - | otherwise -> TVariable - AConLike con -> case con of - RealDataCon _ -> TDataCon - PatSynCon _ -> TPatternSyn - ATyCon tyCon - | isTypeSynonymTyCon tyCon -> TTypeSyn - | isTypeFamilyTyCon tyCon -> TTypeFamily - | isClassTyCon tyCon -> TClass - -- | isVanillaAlgTyCon tyCon -> TTypeCon - -- | isPrimTyCon tyCon -> TTypeCon - -- fall back to TTypeCon for - -- including defined by data, newtype, and type family instance - | otherwise -> TTypeCon - ACoAxiom _ -> TNothing - -isFunVar :: Var -> Bool -isFunVar var = isFunType $ varType var + AnId vid + | isTyVar vid -> TTypeVariable + | isRecordSelector vid -> TRecField + | isClassOpId vid -> TClassMethod + | isFunVar vid -> TFunction + -- \| isDFunId vid -> TClassMethod + | otherwise -> TVariable + AConLike con -> case con of + RealDataCon _ -> TDataCon + PatSynCon _ -> TPatternSyn + ATyCon tyCon + | isTypeSynonymTyCon tyCon -> TTypeSyn + | isTypeFamilyTyCon tyCon -> TTypeFamily + | isClassTyCon tyCon -> TClass + -- fall back to TTypeCon the result + | otherwise -> TTypeCon + ACoAxiom _ -> TNothing + where + isFunVar :: Var -> Bool + isFunVar var = isFunType $ varType var + isFunType :: Type -> Bool isFunType a = case a of - ForAllTy _ a -> isFunType a - x -> isFunTy a - - -{- | -3. from HIE to our token type --} + ForAllTy _ t -> isFunType t + _x -> isFunTy a typeSemantic :: HieKind hType -> hType -> SemanticTokenType typeSemantic kind t = case kind of - HieFresh -> if isFunType t then TFunction else TNothing - HieFromDisk full_file -> - if isFixFunction fullType - then TFunction - else TNothing - where - fullType = recoverFullType t (hie_types full_file) - + HieFresh -> if isFunType t then TFunction else TNothing + HieFromDisk full_file -> + if isFixFunction fullType + then TFunction + else TNothing where - isFixFunction :: HieTypeFix -> Bool - isFixFunction (Roll x) = - case x of - HForAllTy _ a -> isFixFunction a - HFunTy {} -> True - _ -> False - - + fullType = recoverFullType t (hie_types full_file) + where + isFixFunction :: HieTypeFix -> Bool + isFixFunction (Roll x) = + case x of + HForAllTy _ a -> isFixFunction a + HFunTy {} -> True + _ -> False +-- * 3. Mapping from hieAst ContextInfo to haskell semantic token type. infoTokenType :: ContextInfo -> SemanticTokenType infoTokenType x = case x of - Use -> TNothing - MatchBind -> TNothing - IEThing _ -> TNothing - TyDecl -> TNothing -- type signature - - ValBind RegularBind _ _ -> TVariable - ValBind InstanceBind _ _ -> TClassMethod - PatternBind {} -> TVariable - ClassTyDecl _ -> TClassMethod - TyVarBind _ _ -> TTypeVariable - RecField _ _ -> TRecField - -- data constructor, type constructor, type synonym, type family - Decl ClassDec _ -> TClass - Decl DataDec _ -> TTypeCon - Decl ConDec _ -> TDataCon - Decl SynDec _ -> TTypeSyn - Decl FamDec _ -> TTypeFamily - -- instance dec is class method - Decl InstDec _ -> TClassMethod - Decl PatSynDec _ -> TPatternSyn - - EvidenceVarUse -> TNothing - EvidenceVarBind {} -> TNothing - - --------------------------------- ----- from lsp token to our token --------------------------------- - --- line, startChar, len, tokenType, modifiers + Use -> TNothing + MatchBind -> TNothing + IEThing _ -> TNothing + TyDecl -> TNothing -- type signature + ValBind RegularBind _ _ -> TVariable + ValBind InstanceBind _ _ -> TClassMethod + PatternBind {} -> TVariable + ClassTyDecl _ -> TClassMethod + TyVarBind _ _ -> TTypeVariable + RecField _ _ -> TRecField + -- data constructor, type constructor, type synonym, type family + Decl ClassDec _ -> TClass + Decl DataDec _ -> TTypeCon + Decl ConDec _ -> TDataCon + Decl SynDec _ -> TTypeSyn + Decl FamDec _ -> TTypeFamily + -- instance dec is class method + Decl InstDec _ -> TClassMethod + Decl PatSynDec _ -> TPatternSyn + EvidenceVarUse -> TNothing + EvidenceVarBind {} -> TNothing + +-- * 4. Mapping from LSP tokens to SemanticTokenOriginal. + +-- | line, startChar, len, tokenType, modifiers type ActualToken = (UInt, UInt, UInt, SemanticTokenType, UInt) + -- | recoverSemanticTokens --- used for debug and test +-- for debug and test. -- this function is used to recover the original tokens(with token in haskell token type zoon) -- from the lsp semantic tokens(with token in lsp token type zoon) recoverSemanticTokens :: String -> SemanticTokens -> Either Text [SemanticTokenOriginal] recoverSemanticTokens sourceCode (SemanticTokens _ xs) = fmap (tokenOrigin sourceCode) <$> dataActualToken xs - where - tokenOrigin :: [Char] -> ActualToken -> SemanticTokenOriginal - tokenOrigin sourceCode (line, startChar, len, tokenType, _) = - -- convert back to count from 1 - SemanticTokenOriginal tokenType (Loc (line+1) (startChar+1) len) name - where tLine = lines sourceCode !? fromIntegral line - name = maybe "no source" (take (fromIntegral len) . drop (fromIntegral startChar)) tLine - - dataActualToken :: [UInt] -> Either Text [ActualToken] - dataActualToken xs = maybe decodeError (Right . fmap semanticTokenAbsoluteActualToken . absolutizeTokens) - $ mapM fromTuple (chunksOf 5 $ map fromIntegral xs) - where - decodeError = Left "recoverSemanticTokenRelative: wrong token data" - fromTuple [a, b, c, d, _] = SemanticTokenRelative a b c <$> fromInt (fromIntegral d) <*> return [] - fromTuple _ = Nothing - - semanticTokenAbsoluteActualToken :: SemanticTokenAbsolute -> ActualToken - semanticTokenAbsoluteActualToken (SemanticTokenAbsolute line startChar len tokenType tokenModifiers) = - (line, startChar, len, fromLspTokenType tokenType, 0) - - -- legends :: SemanticTokensLegend - fromInt :: Int -> Maybe SemanticTokenTypes - fromInt i = Set.toAscList knownValues !? i + where + tokenOrigin :: [Char] -> ActualToken -> SemanticTokenOriginal + tokenOrigin sourceCode' (line, startChar, len, tokenType, _) = + -- convert back to count from 1 + SemanticTokenOriginal tokenType (Loc (line + 1) (startChar + 1) len) name + where + tLine = lines sourceCode' !? fromIntegral line + name = maybe "no source" (take (fromIntegral len) . drop (fromIntegral startChar)) tLine + + dataActualToken :: [UInt] -> Either Text [ActualToken] + dataActualToken dt = + maybe decodeError (Right . fmap semanticTokenAbsoluteActualToken . absolutizeTokens) $ + mapM fromTuple (chunksOf 5 $ map fromIntegral dt) + where + decodeError = Left "recoverSemanticTokenRelative: wrong token data" + fromTuple [a, b, c, d, _] = SemanticTokenRelative a b c <$> fromInt (fromIntegral d) <*> return [] + fromTuple _ = Nothing + + semanticTokenAbsoluteActualToken :: SemanticTokenAbsolute -> ActualToken + semanticTokenAbsoluteActualToken (SemanticTokenAbsolute line startChar len tokenType _tokenModifiers) = + (line, startChar, len, fromLspTokenType tokenType, 0) + + -- legends :: SemanticTokensLegend + fromInt :: Int -> Maybe SemanticTokenTypes + fromInt i = Set.toAscList knownValues !? i diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 04d1011851..f9317ae2e7 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -7,62 +7,31 @@ {-# LANGUAGE TupleSections #-} module Ide.Plugin.SemanticTokens.Query where -import Control.Arrow (ArrowChoice ((|||)), - (&&&)) -import Control.Monad (foldM, forM) -import Control.Monad.IO.Class (MonadIO (liftIO)) -import Data.Char (isAlphaNum) -import Data.Either (rights) -import Data.Function (on) -import Data.Generics (everything) -import qualified Data.HashSet as HashSet -import qualified Data.List as List -import qualified Data.List.NonEmpty as NE -import Data.Map (Map) -import qualified Data.Map as Map -import Data.Maybe (catMaybes, fromMaybe, - listToMaybe, mapMaybe, - maybeToList) -import Data.Ord (comparing) -import qualified Data.Set as Set -import Data.Text (Text) -import qualified Data.Text as Text -import qualified Data.Text.Lazy.Builder as Text -import Data.Tuple (swap) -import Debug.Trace -import Development.IDE (Action, HieKind, - filePathToUri', - rangeToRealSrcSpan, - realSpan, - realSrcSpanToRange) +import Data.Either (rights) +import Data.Generics (everything) +import qualified Data.List as List +import qualified Data.List.NonEmpty as NE +import qualified Data.Map as Map +import Data.Maybe (catMaybes, listToMaybe, + mapMaybe) +import qualified Data.Set as Set +import Data.Text (Text) +import Development.IDE (HieKind, + realSrcSpanToRange) import Development.IDE.GHC.Compat -import Development.IDE.GHC.Error (positionToRealSrcLoc) -import Development.IDE.Spans.AtPoint (FOIReferences) -import Development.IDE.Spans.LocalBindings (Bindings) -import Development.IDE.Types.Exports (ExportsMap (getModuleExportsMap), - IdentInfo) -import Development.IDE.Types.Location (toNormalizedFilePath') -import Development.IDE.Types.Shake (WithHieDb) -import Generics.SYB (mkQ) +import Generics.SYB (mkQ) -- import HieDb.Types (DefRow (..)) -import qualified Data.Map as M -import Data.Monoid (First (..)) -import qualified Data.Set as S -import HieDb (DefRow (..), - HieDbErr (AmbiguousUnitId, NameNotFound, NameUnhelpfulSpan, NoNameAtPoint, NotIndexed), - ModuleInfo (modInfoSrcFile), - RefRow (..), Res, - findOneDef, - findReferences, - type (:.) (..)) +import qualified Data.Map as M +import qualified Data.Set as S import Ide.Plugin.SemanticTokens.Mappings -import Ide.Plugin.SemanticTokens.Types -import Ide.Plugin.SemanticTokens.Utils (showName) +import Ide.Plugin.SemanticTokens.Types (NameSemanticMap, + SemanticTokenType) import Language.LSP.Protocol.Types +import Prelude hiding (span) ------------------------------------------------ ---- | extract semantic tokens from RenamedSource +-- * extract names from RenamedSource ------------------------------------------------ nameToCollect :: Name -> NameSet @@ -71,26 +40,25 @@ nameToCollect = unitNameSet nameGetter :: RenamedSource -> NameSet nameGetter = everything unionNameSet (emptyNameSet `mkQ` nameToCollect) --------------------------------------------------- ---- | extract semantic map from HieAst for local variables --------------------------------------------------- +--------------------------------------------------------- +-- * extract semantic map from HieAst for local variables +--------------------------------------------------------- -mkLocalNameSemanticFromAst :: HieKind a -> HieAST a -> RefMap a -> NameSemanticMap -mkLocalNameSemanticFromAst hieKind ast rm = mkNameEnv (mapMaybe (nameNameSemanticFromHie hieKind ast rm) ns) +mkLocalNameSemanticFromAst :: HieKind a -> RefMap a -> NameSemanticMap +mkLocalNameSemanticFromAst hieKind rm = mkNameEnv (mapMaybe (nameNameSemanticFromHie hieKind rm) ns) where ns = rights $ M.keys rm -nameNameSemanticFromHie :: forall a. HieKind a -> HieAST a -> RefMap a -> Name -> Maybe (Name,SemanticTokenType) -nameNameSemanticFromHie hieKind hie rm ns = do +nameNameSemanticFromHie :: forall a. HieKind a -> RefMap a -> Name -> Maybe (Name,SemanticTokenType) +nameNameSemanticFromHie hieKind rm ns = do st <- -- traceShow ("to find Name", showName ns) $ nameSemanticFromRefMap rm ns return -- $ traceShow (showName ns, st) (ns, st) where nameSemanticFromRefMap :: RefMap a -> Name -> Maybe SemanticTokenType - nameSemanticFromRefMap rm name = do - let nameString = showName name + nameSemanticFromRefMap rm' name' = do spanInfos <- -- traceShow ("getting spans:", nameString) $ - Map.lookup (Right name) rm + Map.lookup (Right name') rm' let infos = S.unions $ map (identInfo . snd) spanInfos let typeTokenType = fmap (typeSemantic hieKind) $ listToMaybe $ mapMaybe (identType . snd) spanInfos let contextInfoTokenType = contextInfosMaybeTokenType infos @@ -101,16 +69,16 @@ nameNameSemanticFromHie hieKind hie rm ns = do Just infos -> Just $ maximum $ NE.map infoTokenType infos Nothing -> Nothing --------------------------------------------------- ---- | extract location from HieAST a --------------------------------------------------- +----------------------------------- +-- * extract location from HieAST a +----------------------------------- hieAstSpanNames :: UniqSet Name -> HieAST a -> [(Range, Name)] hieAstSpanNames nameSet ast = if null (nodeChildren ast) then getIds ast else concatMap (hieAstSpanNames nameSet) (nodeChildren ast) where -- getIds :: HieAST a -> [(Span, Name)] - getIds ast = [(realSrcSpanToRange $ nodeSpan ast, c) + getIds ast' = [(realSrcSpanToRange $ nodeSpan ast', c) | (Right c, d) <- Map.toList $ getNodeIds' ast , elemNameSet c nameSet -- at least get one info @@ -130,15 +98,14 @@ hieAstSpanNames nameSet ast = if null (nodeChildren ast) then ad `combineNodeIds` (NodeInfo _ _ bd) = Map.unionWith (<>) ad bd --------------------------------------------------- ---- | extract semantic tokens from NameSemanticMap --------------------------------------------------- +------------------------------------------------- +-- * extract semantic tokens from NameSemanticMap +------------------------------------------------- semanticTokenAbsoluteSemanticTokens :: [SemanticTokenAbsolute] -> Either Text SemanticTokens semanticTokenAbsoluteSemanticTokens = makeSemanticTokens defaultSemanticTokensLegend . List.sort --- extractSemanticTokensFromNames :: NameSemanticMap -> [(Span, Name)] -> [SemanticTokenAbsolute] -extractSemanticTokensFromNames :: UniqFM Name SemanticTokenType -> [(Range, Name)] -> [SemanticTokenAbsolute] +extractSemanticTokensFromNames :: NameSemanticMap -> [(Range, Name)] -> [SemanticTokenAbsolute] extractSemanticTokensFromNames nsm = mapMaybe (uncurry toAbsSemanticToken) . mergeNameFromSamSpan . mapMaybe (getSemantic nsm) where @@ -147,7 +114,7 @@ extractSemanticTokensFromNames nsm = mergeNameFromSamSpan xs = Map.toList $ Map.fromListWith (<>) xs toAbsSemanticToken :: Range -> SemanticTokenType -> Maybe SemanticTokenAbsolute - toAbsSemanticToken (Range (Position startLine startColumn) (Position endLine endColumn)) tokenType = + toAbsSemanticToken (Range (Position startLine startColumn) (Position _endLine endColumn)) tokenType = let len = endColumn - startColumn in SemanticTokenAbsolute (fromIntegral startLine) (fromIntegral startColumn) (fromIntegral len) <$> toLspTokenType tokenType <*> return [] diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 9958d25183..c128fc4dc2 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -1,22 +1,14 @@ -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE StrictData #-} -{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE StrictData #-} +{-# LANGUAGE TypeFamilies #-} module Ide.Plugin.SemanticTokens.Types where -import qualified Data.List as List -import qualified Data.List.Extra as List -import qualified Data.List.NonEmpty as NE -import qualified Data.Map as Map -import Data.Maybe (fromMaybe) -import qualified Data.Set as Set -import Development.IDE.GHC.Compat --- import GHC.Enum (boundedEnumFrom) import Control.DeepSeq (NFData (rnf), rwhnf) import Data.Generics (Typeable) import Development.IDE (RuleResult) +import Development.IDE.GHC.Compat import Development.IDE.Graph.Classes (Hashable) import GHC.Generics (Generic) import Language.LSP.Protocol.Types @@ -47,16 +39,16 @@ instance Monoid SemanticTokenType where mempty = TNothing data SemanticTokenOriginal = SemanticTokenOriginal - { tokenType :: SemanticTokenType, - loc :: Loc, - name :: String + { _tokenType :: SemanticTokenType, + _loc :: Loc, + _name :: String } deriving (Show, Eq, Ord) data Loc = Loc - { line :: UInt, - startChar :: UInt, - len :: UInt + { _line :: UInt, + _startChar :: UInt, + _len :: UInt } deriving (Show, Eq, Ord) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs index 8917959e6a..971069d1e8 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs @@ -1,6 +1,6 @@ --- stand alone deriving {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeFamilies #-} +{-# OPTIONS_GHC -Wno-orphans #-} module Ide.Plugin.SemanticTokens.Utils where @@ -10,6 +10,7 @@ import Data.ByteString.Char8 (unpack) import qualified Data.Map as Map import Development.IDE.GHC.Compat import Ide.Plugin.SemanticTokens.Types +import Prelude hiding (span) deriving instance Show DeclType deriving instance Show BindType @@ -21,7 +22,7 @@ instance Show ContextInfo where MatchBind -> "MatchBind" IEThing _ -> "IEThing IEType" -- imported TyDecl -> "TyDecl" - ValBind bt _ span -> "ValBind of " <> show bt <> show span + ValBind bt _ sp -> "ValBind of " <> show bt <> show sp PatternBind {} -> "PatternBind" ClassTyDecl _ -> "ClassTyDecl" Decl d _ -> "Decl of " <> show d @@ -65,6 +66,7 @@ showNameType name | isExternalName name = "ExternalName" | isSystemName name = "SystemName" | isWiredInName name = "WiredInName" + | otherwise = "UnknownName" bytestringString :: ByteString -> String bytestringString = map (toEnum . fromEnum) . unpack diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index b5d979d7ae..8e49c413b1 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-} +{-# LANGUAGE ExplicitNamespaces #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} @@ -8,6 +9,7 @@ import Control.Arrow (Arrow ((***)), (&&&), (+++)) import Control.Lens hiding (use, (<.>)) import Control.Monad (forM) +import Control.Monad.IO.Class (liftIO) import Data.Bifunctor import qualified Data.ByteString as BS import Data.Data @@ -24,6 +26,7 @@ import Data.Text hiding (length, map, import Development.IDE (getFileContents, runAction) import Development.IDE.Core.Rules (Log) import Development.IDE.Plugin.Test (WaitForIdeRuleResult (..)) +import Development.IDE.Test (waitForBuildQueue) import Ide.Plugin.Error (getNormalizedFilePathE) import Ide.Plugin.SemanticTokens import Ide.Plugin.SemanticTokens.Mappings @@ -31,15 +34,24 @@ import Ide.Plugin.SemanticTokens.Types import Ide.Types import qualified Language.LSP.Protocol.Lens as L import Language.LSP.Protocol.Types (SemanticTokens (..), - SemanticTokensParams (..)) + SemanticTokensParams (..), + _L, type (|?) (..)) +import Language.LSP.Test (openDoc) import qualified Language.LSP.Test as Test import System.Environment.Blank import System.FilePath -import qualified Test.Hls (PluginTestDescriptor, +import Test.Hls (PluginTestDescriptor, + Session, TestName, + TestTree, + TextDocumentIdentifier, + defaultTestRunner, + documentContents, + goldenGitDiff, + mkPluginTestDescriptor, mkPluginTestDescriptor', runSessionWithServerInTmpDir, - waitForAction) -import Test.Hls + testCase, testGroup, + waitForAction, (@?=)) import qualified Test.Hls.FileSystem as FS import Test.Hls.Util (withCanonicalTempDir) @@ -55,7 +67,6 @@ semanticTokensPlugin = Test.Hls.mkPluginTestDescriptor Ide.Plugin.SemanticTokens mkSemanticTokensParams :: TextDocumentIdentifier -> SemanticTokensParams mkSemanticTokensParams = SemanticTokensParams Nothing Nothing - goldenWithHaskellAndCapsOutPut config plugin title tree path desc act = goldenGitDiff title (FS.vftOriginalRoot tree path <.> desc) $ runSessionWithServerInTmpDir config plugin tree $ @@ -125,8 +136,8 @@ semanticTokensTests = let file1 = "TModuleA.hs" let file2 = "TModuleB.hs" let expect = - [ SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 1, len = 2}, name = "go"}, - SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 5, startChar = 6, len = 4}, name = "Game"} + [ SemanticTokenOriginal TVariable (Loc 5 1 2) "go", + SemanticTokenOriginal TDataCon (Loc 5 6 4) "Game" ] Test.Hls.runSessionWithServerInTmpDir def semanticTokensPlugin (mkFs $ FS.directProjectMulti [file1, file2]) $ do doc1 <- openDoc file1 "haskell" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected index 07f39f21c4..01c814809c 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected @@ -1,81 +1,81 @@ -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 18, len = 3}, name = "Set"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 23, len = 6}, name = "insert"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 6, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 9, startChar = 12, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 9, startChar = 18, len = 3}, name = "foo"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 9, startChar = 25, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 11, startChar = 7, len = 3}, name = "Boo"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 11, startChar = 11, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 12, startChar = 3, len = 3}, name = "boo"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 10, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 12, startChar = 15, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 14, startChar = 10, len = 3}, name = "Boo"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 14, startChar = 14, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 5, len = 3}, name = "boo"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 15, startChar = 9, len = 1}, name = "x"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 15, startChar = 13, len = 1}, name = "x"} -SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 15, startChar = 15, len = 1}, name = "+"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 6, len = 2}, name = "Dd"} -SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 17, startChar = 11, len = 2}, name = "Dd"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 17, startChar = 14, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 19, startChar = 9, len = 3}, name = "One"} -SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 19, startChar = 15, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 21, startChar = 1, len = 3}, name = "ggg"} -SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 21, startChar = 7, len = 3}, name = "One"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 6, len = 3}, name = "Doo"} -SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 23, startChar = 12, len = 3}, name = "Doo"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 23, startChar = 16, len = 11}, name = "Prelude.Int"} -SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 24, startChar = 6, len = 4}, name = "Bar1"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 24, startChar = 13, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 25, startChar = 6, len = 4}, name = "Bar2"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 25, startChar = 13, len = 3}, name = "Doo"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 27, startChar = 1, len = 2}, name = "bb"} -SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 27, startChar = 8, len = 3}, name = "Boo"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 12, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 18, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 27, startChar = 23, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 28, startChar = 1, len = 2}, name = "bb"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 28, startChar = 4, len = 1}, name = "x"} -SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 28, startChar = 9, len = 3}, name = "boo"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 28, startChar = 13, len = 1}, name = "x"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 29, startChar = 1, len = 2}, name = "aa"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 7, len = 4}, name = "cool"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 29, startChar = 15, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 29, startChar = 22, len = 4}, name = "cool"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 30, startChar = 1, len = 2}, name = "aa"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 4, len = 1}, name = "x"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 9, len = 1}, name = "c"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 30, startChar = 14, len = 2}, name = "aa"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 17, len = 1}, name = "x"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 30, startChar = 19, len = 1}, name = "c"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 31, startChar = 12, len = 2}, name = "xx"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 31, startChar = 16, len = 2}, name = "yy"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 32, startChar = 11, len = 2}, name = "dd"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 34, startChar = 2, len = 2}, name = "zz"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 34, startChar = 6, len = 2}, name = "kk"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 35, startChar = 1, len = 2}, name = "cc"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 7, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 15, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 20, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 35, startChar = 28, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 36, startChar = 1, len = 2}, name = "cc"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 36, startChar = 4, len = 1}, name = "f"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 36, startChar = 7, len = 2}, name = "gg"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 36, startChar = 11, len = 2}, name = "vv"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 37, startChar = 10, len = 2}, name = "gg"} -SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 14, len = 3}, name = "foo"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 38, startChar = 18, len = 1}, name = "$"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 38, startChar = 20, len = 1}, name = "f"} -SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 38, startChar = 24, len = 3}, name = "foo"} -SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 14, len = 3}, name = "foo"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 39, startChar = 18, len = 1}, name = "$"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 39, startChar = 20, len = 1}, name = "f"} -SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 39, startChar = 24, len = 3}, name = "foo"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 41, startChar = 1, len = 2}, name = "go"} -SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 41, startChar = 6, len = 3}, name = "foo"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 42, startChar = 1, len = 3}, name = "add"} -SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 42, startChar = 7, len = 11}, name = "(Prelude.+)"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 47, startChar = 1, len = 4}, name = "main"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 47, startChar = 9, len = 2}, name = "IO"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 48, startChar = 1, len = 4}, name = "main"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 48, startChar = 8, len = 8}, name = "putStrLn"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 6, _startChar = 18, _len = 3}, _name = "Set"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 6, _startChar = 23, _len = 6}, _name = "insert"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 9, _startChar = 6, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 9, _startChar = 12, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TRecField, _loc = Loc {_line = 9, _startChar = 18, _len = 3}, _name = "foo"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 9, _startChar = 25, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TClass, _loc = Loc {_line = 11, _startChar = 7, _len = 3}, _name = "Boo"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 11, _startChar = 11, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 12, _startChar = 3, _len = 3}, _name = "boo"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 12, _startChar = 10, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 12, _startChar = 15, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TClass, _loc = Loc {_line = 14, _startChar = 10, _len = 3}, _name = "Boo"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 14, _startChar = 14, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 15, _startChar = 5, _len = 3}, _name = "boo"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 15, _startChar = 9, _len = 1}, _name = "x"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 15, _startChar = 13, _len = 1}, _name = "x"} +SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 15, _startChar = 15, _len = 1}, _name = "+"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 17, _startChar = 6, _len = 2}, _name = "Dd"} +SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 17, _startChar = 11, _len = 2}, _name = "Dd"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 17, _startChar = 14, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TPatternSyn, _loc = Loc {_line = 19, _startChar = 9, _len = 3}, _name = "One"} +SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 19, _startChar = 15, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 21, _startChar = 1, _len = 3}, _name = "ggg"} +SemanticTokenOriginal {_tokenType = TPatternSyn, _loc = Loc {_line = 21, _startChar = 7, _len = 3}, _name = "One"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 23, _startChar = 6, _len = 3}, _name = "Doo"} +SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 23, _startChar = 12, _len = 3}, _name = "Doo"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 23, _startChar = 16, _len = 11}, _name = "Prelude.Int"} +SemanticTokenOriginal {_tokenType = TTypeSyn, _loc = Loc {_line = 24, _startChar = 6, _len = 4}, _name = "Bar1"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 24, _startChar = 13, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TTypeSyn, _loc = Loc {_line = 25, _startChar = 6, _len = 4}, _name = "Bar2"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 25, _startChar = 13, _len = 3}, _name = "Doo"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 27, _startChar = 1, _len = 2}, _name = "bb"} +SemanticTokenOriginal {_tokenType = TClass, _loc = Loc {_line = 27, _startChar = 8, _len = 3}, _name = "Boo"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 27, _startChar = 12, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 27, _startChar = 18, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 27, _startChar = 23, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 28, _startChar = 1, _len = 2}, _name = "bb"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 28, _startChar = 4, _len = 1}, _name = "x"} +SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 28, _startChar = 9, _len = 3}, _name = "boo"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 28, _startChar = 13, _len = 1}, _name = "x"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 29, _startChar = 1, _len = 2}, _name = "aa"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 29, _startChar = 7, _len = 4}, _name = "cool"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 29, _startChar = 15, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 29, _startChar = 22, _len = 4}, _name = "cool"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 30, _startChar = 1, _len = 2}, _name = "aa"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 30, _startChar = 4, _len = 1}, _name = "x"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 30, _startChar = 9, _len = 1}, _name = "c"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 30, _startChar = 14, _len = 2}, _name = "aa"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 30, _startChar = 17, _len = 1}, _name = "x"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 30, _startChar = 19, _len = 1}, _name = "c"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 31, _startChar = 12, _len = 2}, _name = "xx"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 31, _startChar = 16, _len = 2}, _name = "yy"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 32, _startChar = 11, _len = 2}, _name = "dd"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 34, _startChar = 2, _len = 2}, _name = "zz"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 34, _startChar = 6, _len = 2}, _name = "kk"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 35, _startChar = 1, _len = 2}, _name = "cc"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 35, _startChar = 7, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 35, _startChar = 15, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 35, _startChar = 20, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 35, _startChar = 28, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 36, _startChar = 1, _len = 2}, _name = "cc"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 36, _startChar = 4, _len = 1}, _name = "f"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 36, _startChar = 7, _len = 2}, _name = "gg"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 36, _startChar = 11, _len = 2}, _name = "vv"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 37, _startChar = 10, _len = 2}, _name = "gg"} +SemanticTokenOriginal {_tokenType = TRecField, _loc = Loc {_line = 38, _startChar = 14, _len = 3}, _name = "foo"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 38, _startChar = 18, _len = 1}, _name = "$"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 38, _startChar = 20, _len = 1}, _name = "f"} +SemanticTokenOriginal {_tokenType = TRecField, _loc = Loc {_line = 38, _startChar = 24, _len = 3}, _name = "foo"} +SemanticTokenOriginal {_tokenType = TRecField, _loc = Loc {_line = 39, _startChar = 14, _len = 3}, _name = "foo"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 39, _startChar = 18, _len = 1}, _name = "$"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 39, _startChar = 20, _len = 1}, _name = "f"} +SemanticTokenOriginal {_tokenType = TRecField, _loc = Loc {_line = 39, _startChar = 24, _len = 3}, _name = "foo"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 41, _startChar = 1, _len = 2}, _name = "go"} +SemanticTokenOriginal {_tokenType = TRecField, _loc = Loc {_line = 41, _startChar = 6, _len = 3}, _name = "foo"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 42, _startChar = 1, _len = 3}, _name = "add"} +SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 42, _startChar = 7, _len = 11}, _name = "(Prelude.+)"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 47, _startChar = 1, _len = 4}, _name = "main"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 47, _startChar = 9, _len = 2}, _name = "IO"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 48, _startChar = 1, _len = 4}, _name = "main"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 48, _startChar = 8, _len = 8}, _name = "putStrLn"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TClass.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TClass.expected index e78882a1c1..fde44c924a 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TClass.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TClass.expected @@ -1,5 +1,5 @@ -SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 7, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 4, startChar = 11, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 3, len = 3}, name = "foo"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 10, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 15, len = 3}, name = "Int"} +SemanticTokenOriginal {_tokenType = TClass, _loc = Loc {_line = 4, _startChar = 7, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 4, _startChar = 11, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 5, _startChar = 3, _len = 3}, _name = "foo"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 5, _startChar = 10, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 5, _startChar = 15, _len = 3}, _name = "Int"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.expected index 03002aa1b7..c963659b4c 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.expected @@ -1,3 +1,3 @@ -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 4, startChar = 26, len = 4}, name = "Show"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 4, _startChar = 6, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 4, _startChar = 12, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TClass, _loc = Loc {_line = 4, _startChar = 26, _len = 4}, _name = "Show"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TDataFamily.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TDataFamily.expected index c66bef2ecd..afabc953d4 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TDataFamily.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TDataFamily.expected @@ -1,12 +1,12 @@ -SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 5, startChar = 13, len = 5}, name = "XList"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 5, startChar = 19, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 8, startChar = 15, len = 5}, name = "XList"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 21, len = 4}, name = "Char"} -SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 8, startChar = 28, len = 5}, name = "XCons"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 35, len = 4}, name = "Char"} -SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 8, startChar = 42, len = 5}, name = "XList"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 8, startChar = 48, len = 4}, name = "Char"} -SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 8, startChar = 56, len = 4}, name = "XNil"} -SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 11, startChar = 15, len = 5}, name = "XList"} -SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 11, startChar = 26, len = 9}, name = "XListUnit"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 11, startChar = 37, len = 3}, name = "Int"} +SemanticTokenOriginal {_tokenType = TTypeFamily, _loc = Loc {_line = 5, _startChar = 13, _len = 5}, _name = "XList"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 5, _startChar = 19, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TTypeFamily, _loc = Loc {_line = 8, _startChar = 15, _len = 5}, _name = "XList"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 8, _startChar = 21, _len = 4}, _name = "Char"} +SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 8, _startChar = 28, _len = 5}, _name = "XCons"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 8, _startChar = 35, _len = 4}, _name = "Char"} +SemanticTokenOriginal {_tokenType = TTypeFamily, _loc = Loc {_line = 8, _startChar = 42, _len = 5}, _name = "XList"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 8, _startChar = 48, _len = 4}, _name = "Char"} +SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 8, _startChar = 56, _len = 4}, _name = "XNil"} +SemanticTokenOriginal {_tokenType = TTypeFamily, _loc = Loc {_line = 11, _startChar = 15, _len = 5}, _name = "XList"} +SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 11, _startChar = 26, _len = 9}, _name = "XListUnit"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 11, _startChar = 37, _len = 3}, _name = "Int"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TDataType.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TDataType.expected index 69c7df3b19..eb7e17e295 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TDataType.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TDataType.expected @@ -1,4 +1,4 @@ -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 3, startChar = 12, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 16, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 3, startChar = 30, len = 2}, name = "Eq"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 3, _startChar = 6, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 3, _startChar = 12, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 3, _startChar = 16, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TClass, _loc = Loc {_line = 3, _startChar = 30, _len = 2}, _name = "Eq"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.expected index 36fa8306f9..8af2a8f7e1 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.expected @@ -1,4 +1,4 @@ -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 1, len = 2}, name = "go"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 7, len = 2}, name = "IO"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 1, len = 2}, name = "go"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 6, len = 5}, name = "print"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 5, _startChar = 1, _len = 2}, _name = "go"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 5, _startChar = 7, _len = 2}, _name = "IO"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 6, _startChar = 1, _len = 2}, _name = "go"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 6, _startChar = 6, _len = 5}, _name = "print"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected index ed4b9a455a..3b89ab1bf2 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected @@ -1,11 +1,11 @@ -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 3, startChar = 1, len = 1}, name = "f"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 3, startChar = 13, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 3, startChar = 16, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 3, startChar = 21, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "f"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 3, len = 1}, name = "x"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 7, len = 1}, name = "x"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 1, len = 1}, name = "x"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 6, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 7, startChar = 1, len = 1}, name = "x"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 7, startChar = 5, len = 9}, name = "undefined"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 3, _startChar = 1, _len = 1}, _name = "f"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 3, _startChar = 13, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 3, _startChar = 16, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 3, _startChar = 21, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 4, _startChar = 1, _len = 1}, _name = "f"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 4, _startChar = 3, _len = 1}, _name = "x"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 4, _startChar = 7, _len = 1}, _name = "x"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 6, _startChar = 1, _len = 1}, _name = "x"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 6, _startChar = 6, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 7, _startChar = 1, _len = 1}, _name = "x"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 7, _startChar = 5, _len = 9}, _name = "undefined"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.expected index 42009d78be..d79cd0a17b 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.expected @@ -1,5 +1,5 @@ -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 3, startChar = 1, len = 1}, name = "y"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 1, len = 1}, name = "y"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 9, len = 1}, name = "f"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 11, len = 1}, name = "x"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 3, _startChar = 1, _len = 1}, _name = "y"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 3, _startChar = 6, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 4, _startChar = 1, _len = 1}, _name = "y"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 4, _startChar = 9, _len = 1}, _name = "f"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 4, _startChar = 11, _len = 1}, _name = "x"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.expected index 3a8fb780d8..40119a690e 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.expected @@ -1,7 +1,7 @@ -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 3, startChar = 1, len = 1}, name = "f"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 6, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 3, startChar = 13, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "f"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 7, len = 1}, name = "g"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 5, len = 1}, name = "g"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 6, startChar = 7, len = 1}, name = "x"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 3, _startChar = 1, _len = 1}, _name = "f"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 3, _startChar = 6, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 3, _startChar = 13, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 4, _startChar = 1, _len = 1}, _name = "f"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 4, _startChar = 7, _len = 1}, _name = "g"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 6, _startChar = 5, _len = 1}, _name = "g"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 6, _startChar = 7, _len = 1}, _name = "x"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.expected index 3f5196f85c..ddc2800edd 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.expected @@ -1,13 +1,13 @@ -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 6, len = 3}, name = "Lam"} -SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 6, startChar = 3, len = 4}, name = "Lift"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 11, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 6, startChar = 36, len = 3}, name = "Lam"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 40, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 7, startChar = 3, len = 3}, name = "Lam"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 12, len = 3}, name = "Lam"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 16, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 21, len = 3}, name = "Lam"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 25, len = 1}, name = "b"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 7, startChar = 36, len = 3}, name = "Lam"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 41, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 7, startChar = 46, len = 1}, name = "b"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 5, _startChar = 6, _len = 3}, _name = "Lam"} +SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 6, _startChar = 3, _len = 4}, _name = "Lift"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 6, _startChar = 11, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 6, _startChar = 36, _len = 3}, _name = "Lam"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 6, _startChar = 40, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 7, _startChar = 3, _len = 3}, _name = "Lam"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 7, _startChar = 12, _len = 3}, _name = "Lam"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 7, _startChar = 16, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 7, _startChar = 21, _len = 3}, _name = "Lam"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 7, _startChar = 25, _len = 1}, _name = "b"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 7, _startChar = 36, _len = 3}, _name = "Lam"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 7, _startChar = 41, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 7, _startChar = 46, _len = 1}, _name = "b"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected index 99e875fcc3..e3ca119aa8 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected @@ -1,7 +1,7 @@ -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 16, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TClass, loc = Loc {line = 5, startChar = 10, len = 2}, name = "Eq"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 6, startChar = 5, len = 4}, name = "(==)"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 6, startChar = 12, len = 9}, name = "undefined"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 4, _startChar = 6, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 4, _startChar = 12, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 4, _startChar = 16, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TClass, _loc = Loc {_line = 5, _startChar = 10, _len = 2}, _name = "Eq"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 5, _startChar = 13, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 6, _startChar = 5, _len = 4}, _name = "(==)"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 6, _startChar = 12, _len = 9}, _name = "undefined"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.expected index b1ead5dab2..de68a00a98 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.expected @@ -1,2 +1,2 @@ -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 2}, name = "go"} -SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 4, startChar = 9, len = 4}, name = "(==)"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 4, _startChar = 1, _len = 2}, _name = "go"} +SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 4, _startChar = 9, _len = 4}, _name = "(==)"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.expected index 657e07536a..a61ced4f11 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.expected @@ -1,2 +1,2 @@ -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 4, startChar = 1, len = 1}, name = "g"} -SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 4, len = 7}, name = "Nothing"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 4, _startChar = 1, _len = 1}, _name = "g"} +SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 4, _startChar = 4, _len = 7}, _name = "Nothing"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternSyn.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternSyn.expected index 435e2d0406..29c07ec89d 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternSyn.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternSyn.expected @@ -1 +1 @@ -SemanticTokenOriginal {tokenType = TPatternSyn, loc = Loc {line = 5, startChar = 9, len = 3}, name = "Foo"} +SemanticTokenOriginal {_tokenType = TPatternSyn, _loc = Loc {_line = 5, _startChar = 9, _len = 3}, _name = "Foo"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected index 098267a315..bc80514c97 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected @@ -1,7 +1,7 @@ -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 3, startChar = 2, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 3, startChar = 5, len = 1}, name = "b"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 5, startChar = 1, len = 1}, name = "f"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 5, startChar = 3, len = 1}, name = "g"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 5, len = 1}, name = "y"} -SemanticTokenOriginal {tokenType = TFunction, loc = Loc {line = 5, startChar = 9, len = 1}, name = "g"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 11, len = 1}, name = "y"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 3, _startChar = 2, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 3, _startChar = 5, _len = 1}, _name = "b"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 5, _startChar = 1, _len = 1}, _name = "f"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 5, _startChar = 3, _len = 1}, _name = "g"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 5, _startChar = 5, _len = 1}, _name = "y"} +SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 5, _startChar = 9, _len = 1}, _name = "g"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 5, _startChar = 11, _len = 1}, _name = "y"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.expected index 6dbccbad40..fd36cf1649 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.expected @@ -1,4 +1,4 @@ -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 6, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TDataCon, loc = Loc {line = 4, startChar = 12, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TRecField, loc = Loc {line = 4, startChar = 18, len = 3}, name = "foo"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 25, len = 3}, name = "Int"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 4, _startChar = 6, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 4, _startChar = 12, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TRecField, _loc = Loc {_line = 4, _startChar = 18, _len = 3}, _name = "foo"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 4, _startChar = 25, _len = 3}, _name = "Int"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.expected index 8d77a75d56..08e1b5a484 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.expected @@ -1,8 +1,8 @@ -SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 4, startChar = 13, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 4, startChar = 17, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 5, startChar = 3, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 7, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 5, startChar = 13, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TTypeFamily, loc = Loc {line = 6, startChar = 3, len = 3}, name = "Foo"} -SemanticTokenOriginal {tokenType = TTypeVariable, loc = Loc {line = 6, startChar = 7, len = 1}, name = "a"} -SemanticTokenOriginal {tokenType = TTypeSyn, loc = Loc {line = 6, startChar = 11, len = 6}, name = "String"} +SemanticTokenOriginal {_tokenType = TTypeFamily, _loc = Loc {_line = 4, _startChar = 13, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 4, _startChar = 17, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TTypeFamily, _loc = Loc {_line = 5, _startChar = 3, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 5, _startChar = 7, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 5, _startChar = 13, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TTypeFamily, _loc = Loc {_line = 6, _startChar = 3, _len = 3}, _name = "Foo"} +SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 6, _startChar = 7, _len = 1}, _name = "a"} +SemanticTokenOriginal {_tokenType = TTypeSyn, _loc = Loc {_line = 6, _startChar = 11, _len = 6}, _name = "String"} diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.expected index 0fae7b121e..02c566b972 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.expected @@ -1,4 +1,4 @@ -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 4, startChar = 1, len = 5}, name = "hello"} -SemanticTokenOriginal {tokenType = TTypeCon, loc = Loc {line = 4, startChar = 10, len = 3}, name = "Int"} -SemanticTokenOriginal {tokenType = TVariable, loc = Loc {line = 5, startChar = 1, len = 5}, name = "hello"} -SemanticTokenOriginal {tokenType = TClassMethod, loc = Loc {line = 5, startChar = 9, len = 6}, name = "length"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 4, _startChar = 1, _len = 5}, _name = "hello"} +SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 4, _startChar = 10, _len = 3}, _name = "Int"} +SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 5, _startChar = 1, _len = 5}, _name = "hello"} +SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 5, _startChar = 9, _len = 6}, _name = "length"} From 6114fe50310584a0642c82fc5f3d31dbca1f630f Mon Sep 17 00:00:00 2001 From: Patrick Wales Date: Fri, 29 Dec 2023 10:48:21 +0800 Subject: [PATCH 49/73] Update plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs Co-authored-by: Michael Peyton Jones --- .../src/Ide/Plugin/SemanticTokens/Query.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index f9317ae2e7..93c1655d09 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -119,8 +119,8 @@ extractSemanticTokensFromNames nsm = in SemanticTokenAbsolute (fromIntegral startLine) (fromIntegral startColumn) (fromIntegral len) <$> toLspTokenType tokenType <*> return [] getSemantic nameMap (span, name) = do - let tokenType = lookupNameEnv nameMap name - fmap (span,) tokenType + tokenType <- lookupNameEnv nameMap name + pure (span, tokenType) From 3f158179fb11a6685f7dcde77126ebff09c9738b Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 29 Dec 2023 10:50:24 +0800 Subject: [PATCH 50/73] refactor: remove TNothing and compact the test output --- .../src/Ide/Plugin/SemanticTokens.hs | 1 + .../src/Ide/Plugin/SemanticTokens/Internal.hs | 2 +- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 109 ++++++------ .../src/Ide/Plugin/SemanticTokens/Query.hs | 10 +- .../src/Ide/Plugin/SemanticTokens/Types.hs | 66 +++---- .../test/testdata/T1.expected | 162 +++++++++--------- .../test/testdata/TClass.expected | 10 +- .../testdata/TClassImportedDeriving.expected | 6 +- .../test/testdata/TDataFamily.expected | 24 +-- .../test/testdata/TDataType.expected | 8 +- .../test/testdata/TDatatypeImported.expected | 8 +- .../test/testdata/TFunction.expected | 22 +-- .../test/testdata/TFunctionLet.expected | 10 +- .../test/testdata/TFunctionLocal.expected | 14 +- .../test/testdata/TGADT.expected | 26 +-- .../TInstanceClassMethodBind.expected | 14 +- .../testdata/TInstanceClassMethodUse.expected | 4 +- .../test/testdata/TPatternMatch.expected | 4 +- .../test/testdata/TPatternSyn.expected | 2 +- .../test/testdata/TPatternbind.expected | 14 +- .../test/testdata/TRecord.expected | 8 +- .../test/testdata/TTypefamily.expected | 16 +- .../test/testdata/TValBind.expected | 8 +- 23 files changed, 279 insertions(+), 269 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index d77d1ba28a..fa97d45d79 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -1,6 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} module Ide.Plugin.SemanticTokens (descriptor) where +import Data.List import Development.IDE import Development.IDE.Core.Rules (Log) import qualified Ide.Plugin.SemanticTokens.Internal as Internal diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index c4573f0bc1..b714e843b8 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -122,7 +122,7 @@ getImportedNameSemanticRule recorder = getTypeExclude localMap env n nameMap | Nothing <- lookupNameEnv localMap n = do tyThing <- lookupImported env n - pure $ maybe nameMap (extendNameEnv nameMap n . tyThingSemantic) tyThing + pure $ maybe nameMap (extendNameEnv nameMap n) (tyThing >>= tyThingSemantic) | otherwise = pure nameMap lookupImported :: HscEnv -> Name -> IO (Maybe TyThing) lookupImported env = fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 9859bee497..de3b367ce5 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -14,7 +14,6 @@ module Ide.Plugin.SemanticTokens.Mappings where import Data.List.Extra (chunksOf, (!?)) import qualified Data.Map as Map -import Data.Maybe (fromMaybe, mapMaybe) import qualified Data.Set as Set import Data.Text (Text) import Development.IDE (HieKind (HieFresh, HieFromDisk)) @@ -30,34 +29,34 @@ import Language.LSP.Protocol.Types (LspEnum (knownValues), -- * 1. Mapping semantic token type to and from the LSP default token type. -- | map from haskell semantic token type to LSP default token type -toLspTokenType :: SemanticTokenType -> Maybe SemanticTokenTypes +toLspTokenType :: SemanticTokenType -> SemanticTokenTypes toLspTokenType tk = case tk of -- TVariable -> SemanticTokenTypes_Variable -- left hand side of none pattern bind - TFunction -> Just SemanticTokenTypes_Function - TVariable -> Just SemanticTokenTypes_Variable - TClass -> Just SemanticTokenTypes_Class - TClassMethod -> Just SemanticTokenTypes_Method - TTypeVariable -> Just SemanticTokenTypes_TypeParameter + TFunction -> SemanticTokenTypes_Function + TVariable -> SemanticTokenTypes_Variable + TClass -> SemanticTokenTypes_Class + TClassMethod -> SemanticTokenTypes_Method + TTypeVariable -> SemanticTokenTypes_TypeParameter -- normal data type is a tagged union type look like enum type -- and a record is a product type like struct -- but we don't distinguish them yet - TTypeCon -> Just SemanticTokenTypes_Enum - TDataCon -> Just SemanticTokenTypes_EnumMember - TRecField -> Just SemanticTokenTypes_Property + TTypeCon -> SemanticTokenTypes_Enum + TDataCon -> SemanticTokenTypes_EnumMember + TRecField -> SemanticTokenTypes_Property -- pattern syn is like a limited version of macro of constructing a data type - TPatternSyn -> Just SemanticTokenTypes_Macro + TPatternSyn -> SemanticTokenTypes_Macro -- saturated type - TTypeSyn -> Just SemanticTokenTypes_Type + TTypeSyn -> SemanticTokenTypes_Type -- not sure if this is correct choice - TTypeFamily -> Just SemanticTokenTypes_Interface - TNothing -> Nothing + TTypeFamily -> SemanticTokenTypes_Interface +-- TNothing -> Nothing lspTokenReverseMap :: Map.Map SemanticTokenTypes SemanticTokenType -lspTokenReverseMap = Map.fromList $ mapMaybe (\x -> fmap (,x) (toLspTokenType x)) $ enumFrom minBound +lspTokenReverseMap = Map.fromList $ map (\x -> (toLspTokenType x,x)) $ enumFrom minBound -fromLspTokenType :: SemanticTokenTypes -> SemanticTokenType -fromLspTokenType tk = fromMaybe TNothing $ Map.lookup tk lspTokenReverseMap +fromLspTokenType :: SemanticTokenTypes -> Maybe SemanticTokenType +fromLspTokenType tk = Map.lookup tk lspTokenReverseMap -- * 2. Mapping from GHC type and tyThing to semantic token type. @@ -67,28 +66,28 @@ toTokenType locName = case occNameSpace $ occName locName of x | isTvNameSpace x -> TTypeVariable x | isTcClsNameSpace x -> TTypeCon -- Type constructors and classes in the same name space for now x | isVarNameSpace x -> TVariable - _ -> TNothing + _ -> TVariable -- | tyThingSemantic -tyThingSemantic :: TyThing -> SemanticTokenType +tyThingSemantic :: TyThing -> Maybe SemanticTokenType tyThingSemantic ty = case ty of AnId vid - | isTyVar vid -> TTypeVariable - | isRecordSelector vid -> TRecField - | isClassOpId vid -> TClassMethod - | isFunVar vid -> TFunction + | isTyVar vid -> Just TTypeVariable + | isRecordSelector vid -> Just TRecField + | isClassOpId vid -> Just TClassMethod + | isFunVar vid -> Just TFunction -- \| isDFunId vid -> TClassMethod - | otherwise -> TVariable + | otherwise -> Just TVariable AConLike con -> case con of - RealDataCon _ -> TDataCon - PatSynCon _ -> TPatternSyn + RealDataCon _ -> Just TDataCon + PatSynCon _ -> Just TPatternSyn ATyCon tyCon - | isTypeSynonymTyCon tyCon -> TTypeSyn - | isTypeFamilyTyCon tyCon -> TTypeFamily - | isClassTyCon tyCon -> TClass + | isTypeSynonymTyCon tyCon -> Just TTypeSyn + | isTypeFamilyTyCon tyCon -> Just TTypeFamily + | isClassTyCon tyCon -> Just TClass -- fall back to TTypeCon the result - | otherwise -> TTypeCon - ACoAxiom _ -> TNothing + | otherwise -> Just TTypeCon + ACoAxiom _ -> Nothing where isFunVar :: Var -> Bool isFunVar var = isFunType $ varType var @@ -100,11 +99,11 @@ isFunType a = case a of typeSemantic :: HieKind hType -> hType -> SemanticTokenType typeSemantic kind t = case kind of - HieFresh -> if isFunType t then TFunction else TNothing + HieFresh -> if isFunType t then TFunction else TVariable HieFromDisk full_file -> if isFixFunction fullType then TFunction - else TNothing + else TVariable where fullType = recoverFullType t (hie_types full_file) where @@ -117,29 +116,29 @@ typeSemantic kind t = case kind of -- * 3. Mapping from hieAst ContextInfo to haskell semantic token type. -infoTokenType :: ContextInfo -> SemanticTokenType +infoTokenType :: ContextInfo -> Maybe SemanticTokenType infoTokenType x = case x of - Use -> TNothing - MatchBind -> TNothing - IEThing _ -> TNothing - TyDecl -> TNothing -- type signature - ValBind RegularBind _ _ -> TVariable - ValBind InstanceBind _ _ -> TClassMethod - PatternBind {} -> TVariable - ClassTyDecl _ -> TClassMethod - TyVarBind _ _ -> TTypeVariable - RecField _ _ -> TRecField + Use -> Nothing + MatchBind -> Nothing + IEThing _ -> Nothing + TyDecl -> Nothing -- type signature + ValBind RegularBind _ _ -> Just TVariable + ValBind InstanceBind _ _ -> Just TClassMethod + PatternBind {} -> Just TVariable + ClassTyDecl _ -> Just TClassMethod + TyVarBind _ _ -> Just TTypeVariable + RecField _ _ -> Just TRecField -- data constructor, type constructor, type synonym, type family - Decl ClassDec _ -> TClass - Decl DataDec _ -> TTypeCon - Decl ConDec _ -> TDataCon - Decl SynDec _ -> TTypeSyn - Decl FamDec _ -> TTypeFamily + Decl ClassDec _ -> Just TClass + Decl DataDec _ -> Just TTypeCon + Decl ConDec _ -> Just TDataCon + Decl SynDec _ -> Just TTypeSyn + Decl FamDec _ -> Just TTypeFamily -- instance dec is class method - Decl InstDec _ -> TClassMethod - Decl PatSynDec _ -> TPatternSyn - EvidenceVarUse -> TNothing - EvidenceVarBind {} -> TNothing + Decl InstDec _ -> Just TClassMethod + Decl PatSynDec _ -> Just TPatternSyn + EvidenceVarUse -> Nothing + EvidenceVarBind {} -> Nothing -- * 4. Mapping from LSP tokens to SemanticTokenOriginal. @@ -172,7 +171,9 @@ recoverSemanticTokens sourceCode (SemanticTokens _ xs) = fmap (tokenOrigin sourc semanticTokenAbsoluteActualToken :: SemanticTokenAbsolute -> ActualToken semanticTokenAbsoluteActualToken (SemanticTokenAbsolute line startChar len tokenType _tokenModifiers) = - (line, startChar, len, fromLspTokenType tokenType, 0) + case fromLspTokenType tokenType of + Just t -> (line, startChar, len, t, 0) + Nothing -> error "semanticTokenAbsoluteActualToken: unknown token type" -- legends :: SemanticTokensLegend fromInt :: Int -> Maybe SemanticTokenTypes diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 93c1655d09..7a3537ebfa 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -66,14 +66,14 @@ nameNameSemanticFromHie hieKind rm ns = do contextInfosMaybeTokenType :: Set.Set ContextInfo -> Maybe SemanticTokenType contextInfosMaybeTokenType details = case NE.nonEmpty $ Set.toList details of - Just infos -> Just $ maximum $ NE.map infoTokenType infos + Just infos -> maximum $ NE.map infoTokenType infos Nothing -> Nothing ----------------------------------- -- * extract location from HieAST a ----------------------------------- -hieAstSpanNames :: UniqSet Name -> HieAST a -> [(Range, Name)] +hieAstSpanNames :: NameSet -> HieAST a -> [(Range, Name)] hieAstSpanNames nameSet ast = if null (nodeChildren ast) then getIds ast else concatMap (hieAstSpanNames nameSet) (nodeChildren ast) where @@ -107,17 +107,17 @@ semanticTokenAbsoluteSemanticTokens = makeSemanticTokens defaultSemanticTokensLe extractSemanticTokensFromNames :: NameSemanticMap -> [(Range, Name)] -> [SemanticTokenAbsolute] extractSemanticTokensFromNames nsm = - mapMaybe (uncurry toAbsSemanticToken) . mergeNameFromSamSpan . mapMaybe (getSemantic nsm) + map (uncurry toAbsSemanticToken) . mergeNameFromSamSpan . mapMaybe (getSemantic nsm) where -- merge all tokens with same span -- mergeNameFromSamSpan :: [(Span, SemanticTokenType)] -> [(Span, SemanticTokenType)] mergeNameFromSamSpan xs = Map.toList $ Map.fromListWith (<>) xs - toAbsSemanticToken :: Range -> SemanticTokenType -> Maybe SemanticTokenAbsolute + toAbsSemanticToken :: Range -> SemanticTokenType -> SemanticTokenAbsolute toAbsSemanticToken (Range (Position startLine startColumn) (Position _endLine endColumn)) tokenType = let len = endColumn - startColumn in SemanticTokenAbsolute (fromIntegral startLine) (fromIntegral startColumn) - (fromIntegral len) <$> toLspTokenType tokenType <*> return [] + (fromIntegral len) (toLspTokenType tokenType) [] getSemantic nameMap (span, name) = do tokenType <- lookupNameEnv nameMap name pure (span, tokenType) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index c128fc4dc2..810a534a08 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -4,64 +4,72 @@ module Ide.Plugin.SemanticTokens.Types where - import Control.DeepSeq (NFData (rnf), rwhnf) import Data.Generics (Typeable) import Development.IDE (RuleResult) -import Development.IDE.GHC.Compat +import Development.IDE.GHC.Compat hiding (loc) import Development.IDE.Graph.Classes (Hashable) import GHC.Generics (Generic) import Language.LSP.Protocol.Types - -- !!!! order of declarations matters deriving enum and ord -- since token may come from different source and we want to keep the most specific one -- and we might want to merge them. -data SemanticTokenType = - TNothing -- unknown - | TVariable -- none function variable - | TFunction -- function - | TDataCon -- Data constructor - | TTypeVariable -- Type variable - | TClassMethod -- Class method - | TPatternSyn -- Pattern synonym - | TTypeCon -- Type (Type constructor) - | TClass -- Type class - | TTypeSyn -- Type synonym - | TTypeFamily -- type family - | TRecField -- from match bind - deriving (Eq, Ord, Show, Enum, Bounded) +data SemanticTokenType + = TVariable -- none function variable + | TFunction -- function + | TDataCon -- Data constructor + | TTypeVariable -- Type variable + | TClassMethod -- Class method + | TPatternSyn -- Pattern synonym + | TTypeCon -- Type (Type constructor) + | TClass -- Type class + | TTypeSyn -- Type synonym + | TTypeFamily -- type family + | TRecField -- from match bind + deriving (Eq, Ord, Show, Enum, Bounded) instance Semigroup SemanticTokenType where - -- one in higher enum is more specific - a <> b = max a b -instance Monoid SemanticTokenType where - mempty = TNothing + -- one in higher enum is more specific + a <> b = max a b -data SemanticTokenOriginal = SemanticTokenOriginal +data SemanticTokenOriginal = SemanticTokenOriginal { _tokenType :: SemanticTokenType, _loc :: Loc, _name :: String } - deriving (Show, Eq, Ord) + deriving (Eq, Ord) + +-- 6:23-6:29 TFunction "insert" +-- +instance Show SemanticTokenOriginal where + show (SemanticTokenOriginal tk loc name) = show loc <> " " <> show tk <> " " <> show name data Loc = Loc { _line :: UInt, _startChar :: UInt, _len :: UInt } - deriving (Show, Eq, Ord) + deriving (Eq, Ord) +instance Show Loc where + show (Loc line startChar len) = show line <> ":" <> show startChar <> "-" <> show (startChar + len) type NameSemanticMap = NameEnv SemanticTokenType + data GetGlobalNameSemantic = GetGlobalNameSemantic - deriving (Eq, Show, Typeable, Generic) + deriving (Eq, Show, Typeable, Generic) + instance Hashable GetGlobalNameSemantic -instance NFData GetGlobalNameSemantic -data GlobalTokenTypeMap = GTTMap {importedNameSemanticMap :: !NameSemanticMap } +instance NFData GetGlobalNameSemantic + +data GlobalTokenTypeMap = GTTMap {importedNameSemanticMap :: !NameSemanticMap} + instance NFData GlobalTokenTypeMap where - rnf (GTTMap a) = rwhnf a + rnf (GTTMap a) = rwhnf a + instance Show GlobalTokenTypeMap where - show = const "GlobalNameMap" + show = const "GlobalNameMap" + type instance RuleResult GetGlobalNameSemantic = GlobalTokenTypeMap diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected index 01c814809c..d23585c20e 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected @@ -1,81 +1,81 @@ -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 6, _startChar = 18, _len = 3}, _name = "Set"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 6, _startChar = 23, _len = 6}, _name = "insert"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 9, _startChar = 6, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 9, _startChar = 12, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TRecField, _loc = Loc {_line = 9, _startChar = 18, _len = 3}, _name = "foo"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 9, _startChar = 25, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TClass, _loc = Loc {_line = 11, _startChar = 7, _len = 3}, _name = "Boo"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 11, _startChar = 11, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 12, _startChar = 3, _len = 3}, _name = "boo"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 12, _startChar = 10, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 12, _startChar = 15, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TClass, _loc = Loc {_line = 14, _startChar = 10, _len = 3}, _name = "Boo"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 14, _startChar = 14, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 15, _startChar = 5, _len = 3}, _name = "boo"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 15, _startChar = 9, _len = 1}, _name = "x"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 15, _startChar = 13, _len = 1}, _name = "x"} -SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 15, _startChar = 15, _len = 1}, _name = "+"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 17, _startChar = 6, _len = 2}, _name = "Dd"} -SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 17, _startChar = 11, _len = 2}, _name = "Dd"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 17, _startChar = 14, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TPatternSyn, _loc = Loc {_line = 19, _startChar = 9, _len = 3}, _name = "One"} -SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 19, _startChar = 15, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 21, _startChar = 1, _len = 3}, _name = "ggg"} -SemanticTokenOriginal {_tokenType = TPatternSyn, _loc = Loc {_line = 21, _startChar = 7, _len = 3}, _name = "One"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 23, _startChar = 6, _len = 3}, _name = "Doo"} -SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 23, _startChar = 12, _len = 3}, _name = "Doo"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 23, _startChar = 16, _len = 11}, _name = "Prelude.Int"} -SemanticTokenOriginal {_tokenType = TTypeSyn, _loc = Loc {_line = 24, _startChar = 6, _len = 4}, _name = "Bar1"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 24, _startChar = 13, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TTypeSyn, _loc = Loc {_line = 25, _startChar = 6, _len = 4}, _name = "Bar2"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 25, _startChar = 13, _len = 3}, _name = "Doo"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 27, _startChar = 1, _len = 2}, _name = "bb"} -SemanticTokenOriginal {_tokenType = TClass, _loc = Loc {_line = 27, _startChar = 8, _len = 3}, _name = "Boo"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 27, _startChar = 12, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 27, _startChar = 18, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 27, _startChar = 23, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 28, _startChar = 1, _len = 2}, _name = "bb"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 28, _startChar = 4, _len = 1}, _name = "x"} -SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 28, _startChar = 9, _len = 3}, _name = "boo"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 28, _startChar = 13, _len = 1}, _name = "x"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 29, _startChar = 1, _len = 2}, _name = "aa"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 29, _startChar = 7, _len = 4}, _name = "cool"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 29, _startChar = 15, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 29, _startChar = 22, _len = 4}, _name = "cool"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 30, _startChar = 1, _len = 2}, _name = "aa"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 30, _startChar = 4, _len = 1}, _name = "x"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 30, _startChar = 9, _len = 1}, _name = "c"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 30, _startChar = 14, _len = 2}, _name = "aa"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 30, _startChar = 17, _len = 1}, _name = "x"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 30, _startChar = 19, _len = 1}, _name = "c"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 31, _startChar = 12, _len = 2}, _name = "xx"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 31, _startChar = 16, _len = 2}, _name = "yy"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 32, _startChar = 11, _len = 2}, _name = "dd"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 34, _startChar = 2, _len = 2}, _name = "zz"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 34, _startChar = 6, _len = 2}, _name = "kk"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 35, _startChar = 1, _len = 2}, _name = "cc"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 35, _startChar = 7, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 35, _startChar = 15, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 35, _startChar = 20, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 35, _startChar = 28, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 36, _startChar = 1, _len = 2}, _name = "cc"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 36, _startChar = 4, _len = 1}, _name = "f"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 36, _startChar = 7, _len = 2}, _name = "gg"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 36, _startChar = 11, _len = 2}, _name = "vv"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 37, _startChar = 10, _len = 2}, _name = "gg"} -SemanticTokenOriginal {_tokenType = TRecField, _loc = Loc {_line = 38, _startChar = 14, _len = 3}, _name = "foo"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 38, _startChar = 18, _len = 1}, _name = "$"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 38, _startChar = 20, _len = 1}, _name = "f"} -SemanticTokenOriginal {_tokenType = TRecField, _loc = Loc {_line = 38, _startChar = 24, _len = 3}, _name = "foo"} -SemanticTokenOriginal {_tokenType = TRecField, _loc = Loc {_line = 39, _startChar = 14, _len = 3}, _name = "foo"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 39, _startChar = 18, _len = 1}, _name = "$"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 39, _startChar = 20, _len = 1}, _name = "f"} -SemanticTokenOriginal {_tokenType = TRecField, _loc = Loc {_line = 39, _startChar = 24, _len = 3}, _name = "foo"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 41, _startChar = 1, _len = 2}, _name = "go"} -SemanticTokenOriginal {_tokenType = TRecField, _loc = Loc {_line = 41, _startChar = 6, _len = 3}, _name = "foo"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 42, _startChar = 1, _len = 3}, _name = "add"} -SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 42, _startChar = 7, _len = 11}, _name = "(Prelude.+)"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 47, _startChar = 1, _len = 4}, _name = "main"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 47, _startChar = 9, _len = 2}, _name = "IO"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 48, _startChar = 1, _len = 4}, _name = "main"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 48, _startChar = 8, _len = 8}, _name = "putStrLn"} +6:18-21 TTypeCon "Set" +6:23-29 TFunction "insert" +9:6-9 TTypeCon "Foo" +9:12-15 TDataCon "Foo" +9:18-21 TRecField "foo" +9:25-28 TTypeCon "Int" +11:7-10 TClass "Boo" +11:11-12 TTypeVariable "a" +12:3-6 TClassMethod "boo" +12:10-11 TTypeVariable "a" +12:15-16 TTypeVariable "a" +14:10-13 TClass "Boo" +14:14-17 TTypeCon "Int" +15:5-8 TClassMethod "boo" +15:9-10 TVariable "x" +15:13-14 TVariable "x" +15:15-16 TClassMethod "+" +17:6-8 TTypeCon "Dd" +17:11-13 TDataCon "Dd" +17:14-17 TTypeCon "Int" +19:9-12 TPatternSyn "One" +19:15-18 TDataCon "Foo" +21:1-4 TVariable "ggg" +21:7-10 TPatternSyn "One" +23:6-9 TTypeCon "Doo" +23:12-15 TDataCon "Doo" +23:16-27 TTypeCon "Prelude.Int" +24:6-10 TTypeSyn "Bar1" +24:13-16 TTypeCon "Int" +25:6-10 TTypeSyn "Bar2" +25:13-16 TTypeCon "Doo" +27:1-3 TFunction "bb" +27:8-11 TClass "Boo" +27:12-13 TTypeVariable "a" +27:18-19 TTypeVariable "a" +27:23-24 TTypeVariable "a" +28:1-3 TFunction "bb" +28:4-5 TVariable "x" +28:9-12 TClassMethod "boo" +28:13-14 TVariable "x" +29:1-3 TFunction "aa" +29:7-11 TTypeVariable "cool" +29:15-18 TTypeCon "Int" +29:22-26 TTypeVariable "cool" +30:1-3 TFunction "aa" +30:4-5 TVariable "x" +30:9-10 TVariable "c" +30:14-16 TFunction "aa" +30:17-18 TVariable "x" +30:19-20 TVariable "c" +31:12-14 TVariable "xx" +31:16-18 TVariable "yy" +32:11-13 TVariable "dd" +34:2-4 TVariable "zz" +34:6-8 TVariable "kk" +35:1-3 TFunction "cc" +35:7-10 TTypeCon "Foo" +35:15-18 TTypeCon "Int" +35:20-23 TTypeCon "Int" +35:28-31 TTypeCon "Int" +36:1-3 TFunction "cc" +36:4-5 TVariable "f" +36:7-9 TVariable "gg" +36:11-13 TVariable "vv" +37:10-12 TVariable "gg" +38:14-17 TRecField "foo" +38:18-19 TFunction "$" +38:20-21 TVariable "f" +38:24-27 TRecField "foo" +39:14-17 TRecField "foo" +39:18-19 TFunction "$" +39:20-21 TVariable "f" +39:24-27 TRecField "foo" +41:1-3 TFunction "go" +41:6-9 TRecField "foo" +42:1-4 TFunction "add" +42:7-18 TClassMethod "(Prelude.+)" +47:1-5 TVariable "main" +47:9-11 TTypeCon "IO" +48:1-5 TVariable "main" +48:8-16 TFunction "putStrLn" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TClass.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TClass.expected index fde44c924a..d5f6e51002 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TClass.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TClass.expected @@ -1,5 +1,5 @@ -SemanticTokenOriginal {_tokenType = TClass, _loc = Loc {_line = 4, _startChar = 7, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 4, _startChar = 11, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 5, _startChar = 3, _len = 3}, _name = "foo"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 5, _startChar = 10, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 5, _startChar = 15, _len = 3}, _name = "Int"} +4:7-10 TClass "Foo" +4:11-12 TTypeVariable "a" +5:3-6 TClassMethod "foo" +5:10-11 TTypeVariable "a" +5:15-18 TTypeCon "Int" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.expected index c963659b4c..5e9c894bf4 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TClassImportedDeriving.expected @@ -1,3 +1,3 @@ -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 4, _startChar = 6, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 4, _startChar = 12, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TClass, _loc = Loc {_line = 4, _startChar = 26, _len = 4}, _name = "Show"} +4:6-9 TTypeCon "Foo" +4:12-15 TDataCon "Foo" +4:26-30 TClass "Show" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TDataFamily.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TDataFamily.expected index afabc953d4..b2b0c25d18 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TDataFamily.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TDataFamily.expected @@ -1,12 +1,12 @@ -SemanticTokenOriginal {_tokenType = TTypeFamily, _loc = Loc {_line = 5, _startChar = 13, _len = 5}, _name = "XList"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 5, _startChar = 19, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TTypeFamily, _loc = Loc {_line = 8, _startChar = 15, _len = 5}, _name = "XList"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 8, _startChar = 21, _len = 4}, _name = "Char"} -SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 8, _startChar = 28, _len = 5}, _name = "XCons"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 8, _startChar = 35, _len = 4}, _name = "Char"} -SemanticTokenOriginal {_tokenType = TTypeFamily, _loc = Loc {_line = 8, _startChar = 42, _len = 5}, _name = "XList"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 8, _startChar = 48, _len = 4}, _name = "Char"} -SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 8, _startChar = 56, _len = 4}, _name = "XNil"} -SemanticTokenOriginal {_tokenType = TTypeFamily, _loc = Loc {_line = 11, _startChar = 15, _len = 5}, _name = "XList"} -SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 11, _startChar = 26, _len = 9}, _name = "XListUnit"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 11, _startChar = 37, _len = 3}, _name = "Int"} +5:13-18 TTypeFamily "XList" +5:19-20 TTypeVariable "a" +8:15-20 TTypeFamily "XList" +8:21-25 TTypeCon "Char" +8:28-33 TDataCon "XCons" +8:35-39 TTypeCon "Char" +8:42-47 TTypeFamily "XList" +8:48-52 TTypeCon "Char" +8:56-60 TDataCon "XNil" +11:15-20 TTypeFamily "XList" +11:26-35 TDataCon "XListUnit" +11:37-40 TTypeCon "Int" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TDataType.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TDataType.expected index eb7e17e295..f8f844c423 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TDataType.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TDataType.expected @@ -1,4 +1,4 @@ -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 3, _startChar = 6, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 3, _startChar = 12, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 3, _startChar = 16, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TClass, _loc = Loc {_line = 3, _startChar = 30, _len = 2}, _name = "Eq"} +3:6-9 TTypeCon "Foo" +3:12-15 TDataCon "Foo" +3:16-19 TTypeCon "Int" +3:30-32 TClass "Eq" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.expected index 8af2a8f7e1..7c00ac76a2 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TDatatypeImported.expected @@ -1,4 +1,4 @@ -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 5, _startChar = 1, _len = 2}, _name = "go"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 5, _startChar = 7, _len = 2}, _name = "IO"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 6, _startChar = 1, _len = 2}, _name = "go"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 6, _startChar = 6, _len = 5}, _name = "print"} +5:1-3 TVariable "go" +5:7-9 TTypeCon "IO" +6:1-3 TVariable "go" +6:6-11 TFunction "print" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected index 3b89ab1bf2..701247bd5f 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected @@ -1,11 +1,11 @@ -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 3, _startChar = 1, _len = 1}, _name = "f"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 3, _startChar = 13, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 3, _startChar = 16, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 3, _startChar = 21, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 4, _startChar = 1, _len = 1}, _name = "f"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 4, _startChar = 3, _len = 1}, _name = "x"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 4, _startChar = 7, _len = 1}, _name = "x"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 6, _startChar = 1, _len = 1}, _name = "x"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 6, _startChar = 6, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 7, _startChar = 1, _len = 1}, _name = "x"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 7, _startChar = 5, _len = 9}, _name = "undefined"} +3:1-2 TFunction "f" +3:13-14 TTypeVariable "a" +3:16-17 TTypeVariable "a" +3:21-22 TTypeVariable "a" +4:1-2 TFunction "f" +4:3-4 TVariable "x" +4:7-8 TVariable "x" +6:1-2 TVariable "x" +6:6-7 TTypeVariable "a" +7:1-2 TVariable "x" +7:5-14 TFunction "undefined" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.expected index d79cd0a17b..002da409ca 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLet.expected @@ -1,5 +1,5 @@ -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 3, _startChar = 1, _len = 1}, _name = "y"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 3, _startChar = 6, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 4, _startChar = 1, _len = 1}, _name = "y"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 4, _startChar = 9, _len = 1}, _name = "f"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 4, _startChar = 11, _len = 1}, _name = "x"} +3:1-2 TVariable "y" +3:6-9 TTypeCon "Int" +4:1-2 TVariable "y" +4:9-10 TFunction "f" +4:11-12 TVariable "x" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.expected index 40119a690e..74fbb3a6aa 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunctionLocal.expected @@ -1,7 +1,7 @@ -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 3, _startChar = 1, _len = 1}, _name = "f"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 3, _startChar = 6, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 3, _startChar = 13, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 4, _startChar = 1, _len = 1}, _name = "f"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 4, _startChar = 7, _len = 1}, _name = "g"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 6, _startChar = 5, _len = 1}, _name = "g"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 6, _startChar = 7, _len = 1}, _name = "x"} +3:1-2 TFunction "f" +3:6-9 TTypeCon "Int" +3:13-16 TTypeCon "Int" +4:1-2 TFunction "f" +4:7-8 TFunction "g" +6:5-6 TFunction "g" +6:7-8 TVariable "x" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.expected index ddc2800edd..a8a3d37c63 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TGADT.expected @@ -1,13 +1,13 @@ -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 5, _startChar = 6, _len = 3}, _name = "Lam"} -SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 6, _startChar = 3, _len = 4}, _name = "Lift"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 6, _startChar = 11, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 6, _startChar = 36, _len = 3}, _name = "Lam"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 6, _startChar = 40, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 7, _startChar = 3, _len = 3}, _name = "Lam"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 7, _startChar = 12, _len = 3}, _name = "Lam"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 7, _startChar = 16, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 7, _startChar = 21, _len = 3}, _name = "Lam"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 7, _startChar = 25, _len = 1}, _name = "b"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 7, _startChar = 36, _len = 3}, _name = "Lam"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 7, _startChar = 41, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 7, _startChar = 46, _len = 1}, _name = "b"} +5:6-9 TTypeCon "Lam" +6:3-7 TDataCon "Lift" +6:11-12 TTypeVariable "a" +6:36-39 TTypeCon "Lam" +6:40-41 TTypeVariable "a" +7:3-6 TDataCon "Lam" +7:12-15 TTypeCon "Lam" +7:16-17 TTypeVariable "a" +7:21-24 TTypeCon "Lam" +7:25-26 TTypeVariable "b" +7:36-39 TTypeCon "Lam" +7:41-42 TTypeVariable "a" +7:46-47 TTypeVariable "b" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected index e3ca119aa8..e9615920a2 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected @@ -1,7 +1,7 @@ -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 4, _startChar = 6, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 4, _startChar = 12, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 4, _startChar = 16, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TClass, _loc = Loc {_line = 5, _startChar = 10, _len = 2}, _name = "Eq"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 5, _startChar = 13, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 6, _startChar = 5, _len = 4}, _name = "(==)"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 6, _startChar = 12, _len = 9}, _name = "undefined"} +4:6-9 TTypeCon "Foo" +4:12-15 TDataCon "Foo" +4:16-19 TTypeCon "Int" +5:10-12 TClass "Eq" +5:13-16 TTypeCon "Foo" +6:5-9 TClassMethod "(==)" +6:12-21 TFunction "undefined" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.expected index de68a00a98..36e41ff096 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodUse.expected @@ -1,2 +1,2 @@ -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 4, _startChar = 1, _len = 2}, _name = "go"} -SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 4, _startChar = 9, _len = 4}, _name = "(==)"} +4:1-3 TFunction "go" +4:9-13 TClassMethod "(==)" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.expected index a61ced4f11..eb3d90cbc7 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternMatch.expected @@ -1,2 +1,2 @@ -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 4, _startChar = 1, _len = 1}, _name = "g"} -SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 4, _startChar = 4, _len = 7}, _name = "Nothing"} +4:1-2 TFunction "g" +4:4-11 TDataCon "Nothing" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternSyn.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternSyn.expected index 29c07ec89d..11502922e2 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternSyn.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternSyn.expected @@ -1 +1 @@ -SemanticTokenOriginal {_tokenType = TPatternSyn, _loc = Loc {_line = 5, _startChar = 9, _len = 3}, _name = "Foo"} +5:9-12 TPatternSyn "Foo" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected index bc80514c97..6c62634487 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TPatternbind.expected @@ -1,7 +1,7 @@ -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 3, _startChar = 2, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 3, _startChar = 5, _len = 1}, _name = "b"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 5, _startChar = 1, _len = 1}, _name = "f"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 5, _startChar = 3, _len = 1}, _name = "g"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 5, _startChar = 5, _len = 1}, _name = "y"} -SemanticTokenOriginal {_tokenType = TFunction, _loc = Loc {_line = 5, _startChar = 9, _len = 1}, _name = "g"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 5, _startChar = 11, _len = 1}, _name = "y"} +3:2-3 TVariable "a" +3:5-6 TVariable "b" +5:1-2 TFunction "f" +5:3-4 TFunction "g" +5:5-6 TVariable "y" +5:9-10 TFunction "g" +5:11-12 TVariable "y" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.expected index fd36cf1649..683d1c142a 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TRecord.expected @@ -1,4 +1,4 @@ -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 4, _startChar = 6, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TDataCon, _loc = Loc {_line = 4, _startChar = 12, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TRecField, _loc = Loc {_line = 4, _startChar = 18, _len = 3}, _name = "foo"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 4, _startChar = 25, _len = 3}, _name = "Int"} +4:6-9 TTypeCon "Foo" +4:12-15 TDataCon "Foo" +4:18-21 TRecField "foo" +4:25-28 TTypeCon "Int" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.expected index 08e1b5a484..edd5a2a169 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TTypefamily.expected @@ -1,8 +1,8 @@ -SemanticTokenOriginal {_tokenType = TTypeFamily, _loc = Loc {_line = 4, _startChar = 13, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 4, _startChar = 17, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TTypeFamily, _loc = Loc {_line = 5, _startChar = 3, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 5, _startChar = 7, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 5, _startChar = 13, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TTypeFamily, _loc = Loc {_line = 6, _startChar = 3, _len = 3}, _name = "Foo"} -SemanticTokenOriginal {_tokenType = TTypeVariable, _loc = Loc {_line = 6, _startChar = 7, _len = 1}, _name = "a"} -SemanticTokenOriginal {_tokenType = TTypeSyn, _loc = Loc {_line = 6, _startChar = 11, _len = 6}, _name = "String"} +4:13-16 TTypeFamily "Foo" +4:17-18 TTypeVariable "a" +5:3-6 TTypeFamily "Foo" +5:7-10 TTypeCon "Int" +5:13-16 TTypeCon "Int" +6:3-6 TTypeFamily "Foo" +6:7-8 TTypeVariable "a" +6:11-17 TTypeSyn "String" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.expected index 02c566b972..993cf807ef 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TValBind.expected @@ -1,4 +1,4 @@ -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 4, _startChar = 1, _len = 5}, _name = "hello"} -SemanticTokenOriginal {_tokenType = TTypeCon, _loc = Loc {_line = 4, _startChar = 10, _len = 3}, _name = "Int"} -SemanticTokenOriginal {_tokenType = TVariable, _loc = Loc {_line = 5, _startChar = 1, _len = 5}, _name = "hello"} -SemanticTokenOriginal {_tokenType = TClassMethod, _loc = Loc {_line = 5, _startChar = 9, _len = 6}, _name = "length"} +4:1-6 TVariable "hello" +4:10-13 TTypeCon "Int" +5:1-6 TVariable "hello" +5:9-15 TClassMethod "length" From 48237609d872717c65c158a8e9d2531f2d205c55 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 29 Dec 2023 10:59:21 +0800 Subject: [PATCH 51/73] refactor: rename SemanticTokenType to HsSemanticTokenType to avoid confusion with lsp' SemanticTokenTypes --- .../src/Ide/Plugin/SemanticTokens.hs | 1 - .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 16 ++++++++-------- .../src/Ide/Plugin/SemanticTokens/Query.hs | 12 ++++++------ .../src/Ide/Plugin/SemanticTokens/Types.hs | 8 ++++---- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index fa97d45d79..d77d1ba28a 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -1,7 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} module Ide.Plugin.SemanticTokens (descriptor) where -import Data.List import Development.IDE import Development.IDE.Core.Rules (Log) import qualified Ide.Plugin.SemanticTokens.Internal as Internal diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index de3b367ce5..82a21b67ef 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -29,7 +29,7 @@ import Language.LSP.Protocol.Types (LspEnum (knownValues), -- * 1. Mapping semantic token type to and from the LSP default token type. -- | map from haskell semantic token type to LSP default token type -toLspTokenType :: SemanticTokenType -> SemanticTokenTypes +toLspTokenType :: HsSemanticTokenType -> SemanticTokenTypes toLspTokenType tk = case tk of -- TVariable -> SemanticTokenTypes_Variable -- left hand side of none pattern bind @@ -52,15 +52,15 @@ toLspTokenType tk = case tk of TTypeFamily -> SemanticTokenTypes_Interface -- TNothing -> Nothing -lspTokenReverseMap :: Map.Map SemanticTokenTypes SemanticTokenType +lspTokenReverseMap :: Map.Map SemanticTokenTypes HsSemanticTokenType lspTokenReverseMap = Map.fromList $ map (\x -> (toLspTokenType x,x)) $ enumFrom minBound -fromLspTokenType :: SemanticTokenTypes -> Maybe SemanticTokenType +fromLspTokenType :: SemanticTokenTypes -> Maybe HsSemanticTokenType fromLspTokenType tk = Map.lookup tk lspTokenReverseMap -- * 2. Mapping from GHC type and tyThing to semantic token type. -toTokenType :: Name -> SemanticTokenType +toTokenType :: Name -> HsSemanticTokenType toTokenType locName = case occNameSpace $ occName locName of x | isDataConNameSpace x -> TDataCon x | isTvNameSpace x -> TTypeVariable @@ -69,7 +69,7 @@ toTokenType locName = case occNameSpace $ occName locName of _ -> TVariable -- | tyThingSemantic -tyThingSemantic :: TyThing -> Maybe SemanticTokenType +tyThingSemantic :: TyThing -> Maybe HsSemanticTokenType tyThingSemantic ty = case ty of AnId vid | isTyVar vid -> Just TTypeVariable @@ -97,7 +97,7 @@ isFunType a = case a of ForAllTy _ t -> isFunType t _x -> isFunTy a -typeSemantic :: HieKind hType -> hType -> SemanticTokenType +typeSemantic :: HieKind hType -> hType -> HsSemanticTokenType typeSemantic kind t = case kind of HieFresh -> if isFunType t then TFunction else TVariable HieFromDisk full_file -> @@ -116,7 +116,7 @@ typeSemantic kind t = case kind of -- * 3. Mapping from hieAst ContextInfo to haskell semantic token type. -infoTokenType :: ContextInfo -> Maybe SemanticTokenType +infoTokenType :: ContextInfo -> Maybe HsSemanticTokenType infoTokenType x = case x of Use -> Nothing MatchBind -> Nothing @@ -143,7 +143,7 @@ infoTokenType x = case x of -- * 4. Mapping from LSP tokens to SemanticTokenOriginal. -- | line, startChar, len, tokenType, modifiers -type ActualToken = (UInt, UInt, UInt, SemanticTokenType, UInt) +type ActualToken = (UInt, UInt, UInt, HsSemanticTokenType, UInt) -- | recoverSemanticTokens -- for debug and test. diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 7a3537ebfa..64b454edfa 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -24,8 +24,8 @@ import Generics.SYB (mkQ) import qualified Data.Map as M import qualified Data.Set as S import Ide.Plugin.SemanticTokens.Mappings -import Ide.Plugin.SemanticTokens.Types (NameSemanticMap, - SemanticTokenType) +import Ide.Plugin.SemanticTokens.Types (HsSemanticTokenType, + NameSemanticMap) import Language.LSP.Protocol.Types import Prelude hiding (span) @@ -48,14 +48,14 @@ nameGetter = everything unionNameSet (emptyNameSet `mkQ` nameToCollect) mkLocalNameSemanticFromAst :: HieKind a -> RefMap a -> NameSemanticMap mkLocalNameSemanticFromAst hieKind rm = mkNameEnv (mapMaybe (nameNameSemanticFromHie hieKind rm) ns) where ns = rights $ M.keys rm -nameNameSemanticFromHie :: forall a. HieKind a -> RefMap a -> Name -> Maybe (Name,SemanticTokenType) +nameNameSemanticFromHie :: forall a. HieKind a -> RefMap a -> Name -> Maybe (Name,HsSemanticTokenType) nameNameSemanticFromHie hieKind rm ns = do st <- -- traceShow ("to find Name", showName ns) $ nameSemanticFromRefMap rm ns return -- $ traceShow (showName ns, st) (ns, st) where - nameSemanticFromRefMap :: RefMap a -> Name -> Maybe SemanticTokenType + nameSemanticFromRefMap :: RefMap a -> Name -> Maybe HsSemanticTokenType nameSemanticFromRefMap rm' name' = do spanInfos <- -- traceShow ("getting spans:", nameString) $ Map.lookup (Right name') rm' @@ -64,7 +64,7 @@ nameNameSemanticFromHie hieKind rm ns = do let contextInfoTokenType = contextInfosMaybeTokenType infos maximum <$> NE.nonEmpty (catMaybes [typeTokenType, contextInfoTokenType]) - contextInfosMaybeTokenType :: Set.Set ContextInfo -> Maybe SemanticTokenType + contextInfosMaybeTokenType :: Set.Set ContextInfo -> Maybe HsSemanticTokenType contextInfosMaybeTokenType details = case NE.nonEmpty $ Set.toList details of Just infos -> maximum $ NE.map infoTokenType infos Nothing -> Nothing @@ -113,7 +113,7 @@ extractSemanticTokensFromNames nsm = -- mergeNameFromSamSpan :: [(Span, SemanticTokenType)] -> [(Span, SemanticTokenType)] mergeNameFromSamSpan xs = Map.toList $ Map.fromListWith (<>) xs - toAbsSemanticToken :: Range -> SemanticTokenType -> SemanticTokenAbsolute + toAbsSemanticToken :: Range -> HsSemanticTokenType -> SemanticTokenAbsolute toAbsSemanticToken (Range (Position startLine startColumn) (Position _endLine endColumn)) tokenType = let len = endColumn - startColumn in SemanticTokenAbsolute (fromIntegral startLine) (fromIntegral startColumn) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 810a534a08..19c10c33bc 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -15,7 +15,7 @@ import Language.LSP.Protocol.Types -- !!!! order of declarations matters deriving enum and ord -- since token may come from different source and we want to keep the most specific one -- and we might want to merge them. -data SemanticTokenType +data HsSemanticTokenType = TVariable -- none function variable | TFunction -- function | TDataCon -- Data constructor @@ -29,12 +29,12 @@ data SemanticTokenType | TRecField -- from match bind deriving (Eq, Ord, Show, Enum, Bounded) -instance Semigroup SemanticTokenType where +instance Semigroup HsSemanticTokenType where -- one in higher enum is more specific a <> b = max a b data SemanticTokenOriginal = SemanticTokenOriginal - { _tokenType :: SemanticTokenType, + { _tokenType :: HsSemanticTokenType, _loc :: Loc, _name :: String } @@ -55,7 +55,7 @@ data Loc = Loc instance Show Loc where show (Loc line startChar len) = show line <> ":" <> show startChar <> "-" <> show (startChar + len) -type NameSemanticMap = NameEnv SemanticTokenType +type NameSemanticMap = NameEnv HsSemanticTokenType data GetGlobalNameSemantic = GetGlobalNameSemantic deriving (Eq, Show, Typeable, Generic) From 001a91f1627c87c0d2d296da4e122a3a65be8c08 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 29 Dec 2023 12:30:12 +0800 Subject: [PATCH 52/73] refactor: push the computation of semantic token type to getSemanticTokensRule --- .../src/Ide/Plugin/SemanticTokens.hs | 2 +- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 55 +++--- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 20 +-- .../src/Ide/Plugin/SemanticTokens/Query.hs | 159 ++++++++++-------- .../src/Ide/Plugin/SemanticTokens/Types.hs | 17 +- 5 files changed, 129 insertions(+), 124 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index d77d1ba28a..34cc04393b 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -14,7 +14,7 @@ descriptor recorder plId = (defaultPluginDescriptor plId "Provides semantic toke { Ide.Types.pluginHandlers = mkPluginHandler SMethod_TextDocumentSemanticTokensFull Internal.semanticTokensFull , Ide.Types.pluginRules = - Internal.getImportedNameSemanticRule recorder + Internal.getSemanticTokensRule recorder <> Internal.persistentSemanticMapRule } diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index b714e843b8..4e843dbc87 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -16,7 +16,7 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} -module Ide.Plugin.SemanticTokens.Internal (semanticTokensFull, getImportedNameSemanticRule, persistentSemanticMapRule) where +module Ide.Plugin.SemanticTokens.Internal (semanticTokensFull, getSemanticTokensRule, persistentSemanticMapRule) where import Control.Lens ((^.)) import Control.Monad.Except (ExceptT, MonadError (..), @@ -38,7 +38,7 @@ import Development.IDE (Action, cmapWithPrio, define, hieKind, ideLogger, logPriority, - useWithStale_) + useWithStale_, use_) import Development.IDE.Core.Compile (TcModuleResult (..), lookupName) import Development.IDE.Core.PluginUtils (runActionE, @@ -64,9 +64,6 @@ import Prelude hiding (span) logWith :: (MonadIO m) => IdeState -> Priority -> String -> m () logWith st prior = liftIO . logPriority (ideLogger st) prior . T.pack -liftMaybeEither :: (MonadError e m) => e -> Maybe a -> m a -liftMaybeEither e = liftEither . maybe (Left e) Right - ----------------------- ---- the api ----------------------- @@ -75,7 +72,7 @@ liftMaybeEither e = liftEither . maybe (Left e) Right -- -- This function collects information from various sources, including: -- --- Imported name token type from Rule 'GetGlobalNameSemantic' +-- Imported name token type from Rule 'GetSemanticTokens' -- Local names token type from 'hieAst' -- Name locations from 'hieAst' -- Visible names from 'tmrRenamed' @@ -86,21 +83,9 @@ computeSemanticTokens :: IdeState -> NormalizedFilePath -> ExceptT PluginError A computeSemanticTokens state nfp = do let dbg = logWith state Debug dbg $ "Computing semantic tokens for: " <> show nfp - (HAR {..}, mapping) <- useWithStaleE GetHieAst nfp - (TcModuleResult {..}, _) <- useWithStaleE TypeCheck nfp - (GTTMap {importedNameSemanticMap}, _) <- useWithStaleE GetGlobalNameSemantic nfp - (_, ast) <- - liftMaybeEither - (PluginRuleFailed $ "hieAst does not contains ast for:" <> T.pack (show nfp)) - $ listToMaybe - $ Map.toList - $ getAsts hieAst - let nameSet = nameGetter tmrRenamed - -- get current location from the old ones - let spanNames = mapMaybe (\(span, name) -> (,name) <$> toCurrentRange mapping span) $ hieAstSpanNames nameSet ast - let sMap = plusNameEnv_C (<>) importedNameSemanticMap $ mkLocalNameSemanticFromAst hieKind refMap - let moduleAbsTks = extractSemanticTokensFromNames sMap spanNames - withExceptT PluginInternalError $ liftEither $ semanticTokenAbsoluteSemanticTokens moduleAbsTks + (RangeHsSemanticTokenTypes {tokens}, mapping) <- useWithStaleE GetSemanticTokens nfp + let rangeTokens = mapMaybe (\(span, name) -> (,name) <$> toCurrentRange mapping span) tokens + withExceptT PluginInternalError $ liftEither $ semanticTokenAbsoluteSemanticTokens rangeTokens semanticTokensFull :: PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull semanticTokensFull state _ param = do @@ -108,15 +93,25 @@ semanticTokensFull state _ param = do items <- runActionE "SemanticTokens.semanticTokensFull" state $ computeSemanticTokens state nfp return $ InL items --- | Defines the 'getImportedNameSemanticRule' function, which is used to record semantic tokens for imported names. -getImportedNameSemanticRule :: Recorder (WithPriority Log) -> Rules () -getImportedNameSemanticRule recorder = - define (cmapWithPrio LogShake recorder) $ \GetGlobalNameSemantic nfp -> do - (TcModuleResult {..}, _) <- useWithStale_ TypeCheck nfp - (hscEnv -> hsc, _) <- useWithStale_ GhcSessionDeps nfp +-- x :: Int -> Int +-- x i = x i + +-- | Defines the 'getSemanticTokensRule' function, which is used to record semantic tokens for imported names. +getSemanticTokensRule :: Recorder (WithPriority Log) -> Rules () +getSemanticTokensRule recorder = + define (cmapWithPrio LogShake recorder) $ \GetSemanticTokens nfp -> do + (TcModuleResult {..}) <- use_ TypeCheck nfp + (hscEnv -> hsc) <- use_ GhcSessionDeps nfp + (HAR {..}) <- use_ GetHieAst nfp + Just (_, ast) <- return $ listToMaybe $ Map.toList $ getAsts hieAst let nameSet = nameGetter tmrRenamed - tm <- liftIO $ foldrM (getTypeExclude (tcg_type_env tmrTypechecked) hsc) emptyNameEnv $ nameSetElemsStable nameSet - return ([], Just $ GTTMap tm) + -- get imported name semantic map + importedNameSemanticMap <- liftIO $ foldrM (getTypeExclude (tcg_type_env tmrTypechecked) hsc) emptyNameEnv $ nameSetElemsStable nameSet + -- get current location from the old ones + let spanNames = hieAstSpanNames nameSet ast + let sMap = plusNameEnv_C (<>) importedNameSemanticMap $ mkLocalNameSemanticFromAst hieKind refMap + let rangeTokenType = extractSemanticTokensFromNames sMap spanNames + return ([], Just $ RangeHsSemanticTokenTypes rangeTokenType) where -- ignore one already in current module getTypeExclude localMap env n nameMap @@ -130,4 +125,4 @@ getImportedNameSemanticRule recorder = -- | Persistent rule to ensure that semantic tokens doesn't block on startup persistentSemanticMapRule :: Rules () -persistentSemanticMapRule = addPersistentRule GetGlobalNameSemantic $ \_ -> pure $ Just (GTTMap mempty, idDelta, Nothing) +persistentSemanticMapRule = addPersistentRule GetSemanticTokens $ \_ -> pure $ Just (RangeHsSemanticTokenTypes mempty, idDelta, Nothing) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 82a21b67ef..dc256c70f2 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -50,23 +50,17 @@ toLspTokenType tk = case tk of TTypeSyn -> SemanticTokenTypes_Type -- not sure if this is correct choice TTypeFamily -> SemanticTokenTypes_Interface + -- TNothing -> Nothing lspTokenReverseMap :: Map.Map SemanticTokenTypes HsSemanticTokenType -lspTokenReverseMap = Map.fromList $ map (\x -> (toLspTokenType x,x)) $ enumFrom minBound +lspTokenReverseMap = Map.fromList $ map (\x -> (toLspTokenType x, x)) $ enumFrom minBound fromLspTokenType :: SemanticTokenTypes -> Maybe HsSemanticTokenType fromLspTokenType tk = Map.lookup tk lspTokenReverseMap -- * 2. Mapping from GHC type and tyThing to semantic token type. -toTokenType :: Name -> HsSemanticTokenType -toTokenType locName = case occNameSpace $ occName locName of - x | isDataConNameSpace x -> TDataCon - x | isTvNameSpace x -> TTypeVariable - x | isTcClsNameSpace x -> TTypeCon -- Type constructors and classes in the same name space for now - x | isVarNameSpace x -> TVariable - _ -> TVariable -- | tyThingSemantic tyThingSemantic :: TyThing -> Maybe HsSemanticTokenType @@ -94,8 +88,8 @@ tyThingSemantic ty = case ty of isFunType :: Type -> Bool isFunType a = case a of - ForAllTy _ t -> isFunType t - _x -> isFunTy a + ForAllTy _ t -> isFunType t + _x -> isFunTy a typeSemantic :: HieKind hType -> hType -> HsSemanticTokenType typeSemantic kind t = case kind of @@ -171,9 +165,9 @@ recoverSemanticTokens sourceCode (SemanticTokens _ xs) = fmap (tokenOrigin sourc semanticTokenAbsoluteActualToken :: SemanticTokenAbsolute -> ActualToken semanticTokenAbsoluteActualToken (SemanticTokenAbsolute line startChar len tokenType _tokenModifiers) = - case fromLspTokenType tokenType of - Just t -> (line, startChar, len, t, 0) - Nothing -> error "semanticTokenAbsoluteActualToken: unknown token type" + case fromLspTokenType tokenType of + Just t -> (line, startChar, len, t, 0) + Nothing -> error "semanticTokenAbsoluteActualToken: unknown token type" -- legends :: SemanticTokensLegend fromInt :: Int -> Maybe SemanticTokenTypes diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 64b454edfa..79f6443ac1 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -7,120 +7,137 @@ {-# LANGUAGE TupleSections #-} module Ide.Plugin.SemanticTokens.Query where + import Data.Either (rights) import Data.Generics (everything) import qualified Data.List as List import qualified Data.List.NonEmpty as NE +-- import HieDb.Types (DefRow (..)) +import qualified Data.Map as M import qualified Data.Map as Map import Data.Maybe (catMaybes, listToMaybe, mapMaybe) +import qualified Data.Set as S import qualified Data.Set as Set import Data.Text (Text) import Development.IDE (HieKind, realSrcSpanToRange) import Development.IDE.GHC.Compat import Generics.SYB (mkQ) --- import HieDb.Types (DefRow (..)) -import qualified Data.Map as M -import qualified Data.Set as S import Ide.Plugin.SemanticTokens.Mappings import Ide.Plugin.SemanticTokens.Types (HsSemanticTokenType, NameSemanticMap) import Language.LSP.Protocol.Types import Prelude hiding (span) - ------------------------------------------------ + -- * extract names from RenamedSource + ------------------------------------------------ nameToCollect :: Name -> NameSet nameToCollect = unitNameSet nameGetter :: RenamedSource -> NameSet -nameGetter = everything unionNameSet (emptyNameSet `mkQ` nameToCollect) +nameGetter = everything unionNameSet (emptyNameSet `mkQ` nameToCollect) --------------------------------------------------------- + -- * extract semantic map from HieAst for local variables ---------------------------------------------------------- +--------------------------------------------------------- mkLocalNameSemanticFromAst :: HieKind a -> RefMap a -> NameSemanticMap mkLocalNameSemanticFromAst hieKind rm = mkNameEnv (mapMaybe (nameNameSemanticFromHie hieKind rm) ns) - where ns = rights $ M.keys rm -nameNameSemanticFromHie :: forall a. HieKind a -> RefMap a -> Name -> Maybe (Name,HsSemanticTokenType) + where + ns = rights $ M.keys rm + +nameNameSemanticFromHie :: forall a. HieKind a -> RefMap a -> Name -> Maybe (Name, HsSemanticTokenType) nameNameSemanticFromHie hieKind rm ns = do - st <- -- traceShow ("to find Name", showName ns) $ - nameSemanticFromRefMap rm ns - return -- $ traceShow (showName ns, st) - (ns, st) - where - nameSemanticFromRefMap :: RefMap a -> Name -> Maybe HsSemanticTokenType - nameSemanticFromRefMap rm' name' = do - spanInfos <- -- traceShow ("getting spans:", nameString) $ - Map.lookup (Right name') rm' - let infos = S.unions $ map (identInfo . snd) spanInfos - let typeTokenType = fmap (typeSemantic hieKind) $ listToMaybe $ mapMaybe (identType . snd) spanInfos - let contextInfoTokenType = contextInfosMaybeTokenType infos - maximum <$> NE.nonEmpty (catMaybes [typeTokenType, contextInfoTokenType]) - - contextInfosMaybeTokenType :: Set.Set ContextInfo -> Maybe HsSemanticTokenType - contextInfosMaybeTokenType details = case NE.nonEmpty $ Set.toList details of - Just infos -> maximum $ NE.map infoTokenType infos - Nothing -> Nothing + st <- -- traceShow ("to find Name", showName ns) $ + nameSemanticFromRefMap rm ns + return + -- \$ traceShow (showName ns, st) + (ns, st) + where + nameSemanticFromRefMap :: RefMap a -> Name -> Maybe HsSemanticTokenType + nameSemanticFromRefMap rm' name' = do + spanInfos <- -- traceShow ("getting spans:", nameString) $ + Map.lookup (Right name') rm' + let infos = S.unions $ map (identInfo . snd) spanInfos + let typeTokenType = fmap (typeSemantic hieKind) $ listToMaybe $ mapMaybe (identType . snd) spanInfos + let contextInfoTokenType = contextInfosMaybeTokenType infos + maximum <$> NE.nonEmpty (catMaybes [typeTokenType, contextInfoTokenType]) + + contextInfosMaybeTokenType :: Set.Set ContextInfo -> Maybe HsSemanticTokenType + contextInfosMaybeTokenType details = case NE.nonEmpty $ Set.toList details of + Just infos -> maximum $ NE.map infoTokenType infos + Nothing -> Nothing ----------------------------------- + -- * extract location from HieAST a + ----------------------------------- hieAstSpanNames :: NameSet -> HieAST a -> [(Range, Name)] -hieAstSpanNames nameSet ast = if null (nodeChildren ast) then - getIds ast else concatMap (hieAstSpanNames nameSet) (nodeChildren ast) - where - -- getIds :: HieAST a -> [(Span, Name)] - getIds ast' = [(realSrcSpanToRange $ nodeSpan ast', c) - | (Right c, d) <- Map.toList $ getNodeIds' ast - , elemNameSet c nameSet - -- at least get one info - , not $ any isEvidenceBind $ identInfo d - , not $ any isEvidenceUse $ identInfo d - , Set.size (identInfo d) > 0 - -- some derived occName is visible (some fields) - -- , not $ isDerivedOccName (occName c) - ] - getNodeIds' :: HieAST a -> Map.Map Identifier (IdentifierDetails a) - getNodeIds' = Map.foldl' combineNodeIds Map.empty - . Map.filterWithKey (\k _ -> k == SourceInfo) - . getSourcedNodeInfo . sourcedNodeInfo - - combineNodeIds :: Map.Map Identifier (IdentifierDetails a) - -> NodeInfo a -> Map.Map Identifier (IdentifierDetails a) - ad `combineNodeIds` (NodeInfo _ _ bd) = Map.unionWith (<>) ad bd - +hieAstSpanNames nameSet ast = + if null (nodeChildren ast) + then getIds ast + else concatMap (hieAstSpanNames nameSet) (nodeChildren ast) + where + -- getIds :: HieAST a -> [(Span, Name)] + getIds ast' = + [ (realSrcSpanToRange $ nodeSpan ast', c) + | (Right c, d) <- Map.toList $ getNodeIds' ast, + elemNameSet c nameSet, + -- at least get one info + not $ any isEvidenceBind $ identInfo d, + not $ any isEvidenceUse $ identInfo d, + Set.size (identInfo d) > 0 + -- some derived occName is visible (some fields) + -- , not $ isDerivedOccName (occName c) + ] + getNodeIds' :: HieAST a -> Map.Map Identifier (IdentifierDetails a) + getNodeIds' = + Map.foldl' combineNodeIds Map.empty + . Map.filterWithKey (\k _ -> k == SourceInfo) + . getSourcedNodeInfo + . sourcedNodeInfo + + combineNodeIds :: + Map.Map Identifier (IdentifierDetails a) -> + NodeInfo a -> + Map.Map Identifier (IdentifierDetails a) + ad `combineNodeIds` (NodeInfo _ _ bd) = Map.unionWith (<>) ad bd ------------------------------------------------- --- * extract semantic tokens from NameSemanticMap -------------------------------------------------- - -semanticTokenAbsoluteSemanticTokens :: [SemanticTokenAbsolute] -> Either Text SemanticTokens -semanticTokenAbsoluteSemanticTokens = makeSemanticTokens defaultSemanticTokensLegend . List.sort - -extractSemanticTokensFromNames :: NameSemanticMap -> [(Range, Name)] -> [SemanticTokenAbsolute] -extractSemanticTokensFromNames nsm = - map (uncurry toAbsSemanticToken) . mergeNameFromSamSpan . mapMaybe (getSemantic nsm) - where - -- merge all tokens with same span - -- mergeNameFromSamSpan :: [(Span, SemanticTokenType)] -> [(Span, SemanticTokenType)] - mergeNameFromSamSpan xs = Map.toList $ Map.fromListWith (<>) xs - - toAbsSemanticToken :: Range -> HsSemanticTokenType -> SemanticTokenAbsolute - toAbsSemanticToken (Range (Position startLine startColumn) (Position _endLine endColumn)) tokenType = - let len = endColumn - startColumn - in SemanticTokenAbsolute (fromIntegral startLine) (fromIntegral startColumn) - (fromIntegral len) (toLspTokenType tokenType) [] - getSemantic nameMap (span, name) = do - tokenType <- lookupNameEnv nameMap name - pure (span, tokenType) +-- * extract semantic tokens from NameSemanticMap +------------------------------------------------- +semanticTokenAbsoluteSemanticTokens :: [(Range, HsSemanticTokenType)] -> Either Text SemanticTokens +semanticTokenAbsoluteSemanticTokens = makeSemanticTokens defaultSemanticTokensLegend . List.sort . map (uncurry toAbsSemanticToken) + where + toAbsSemanticToken :: Range -> HsSemanticTokenType -> SemanticTokenAbsolute + toAbsSemanticToken (Range (Position startLine startColumn) (Position _endLine endColumn)) tokenType = + let len = endColumn - startColumn + in SemanticTokenAbsolute + (fromIntegral startLine) + (fromIntegral startColumn) + (fromIntegral len) + (toLspTokenType tokenType) + [] + +extractSemanticTokensFromNames :: NameSemanticMap -> [(Range, Name)] -> [(Range, HsSemanticTokenType)] +extractSemanticTokensFromNames nsm = mergeNameFromSamSpan . mapMaybe (getSemantic nsm) + where + -- merge all tokens with same span + -- mergeNameFromSamSpan :: [(Span, SemanticTokenType)] -> [(Span, SemanticTokenType)] + mergeNameFromSamSpan xs = Map.toList $ Map.fromListWith (<>) xs + + getSemantic nameMap (span, name) = do + tokenType <- lookupNameEnv nameMap name + pure (span, tokenType) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 19c10c33bc..0b8a1ba0ba 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -40,7 +40,6 @@ data SemanticTokenOriginal = SemanticTokenOriginal } deriving (Eq, Ord) --- 6:23-6:29 TFunction "insert" -- instance Show SemanticTokenOriginal where show (SemanticTokenOriginal tk loc name) = show loc <> " " <> show tk <> " " <> show name @@ -57,19 +56,19 @@ instance Show Loc where type NameSemanticMap = NameEnv HsSemanticTokenType -data GetGlobalNameSemantic = GetGlobalNameSemantic +data GetSemanticTokens = GetSemanticTokens deriving (Eq, Show, Typeable, Generic) -instance Hashable GetGlobalNameSemantic +instance Hashable GetSemanticTokens -instance NFData GetGlobalNameSemantic +instance NFData GetSemanticTokens -data GlobalTokenTypeMap = GTTMap {importedNameSemanticMap :: !NameSemanticMap} +data RangeHsSemanticTokenTypes = RangeHsSemanticTokenTypes {tokens :: [(Range, HsSemanticTokenType)]} -instance NFData GlobalTokenTypeMap where - rnf (GTTMap a) = rwhnf a +instance NFData RangeHsSemanticTokenTypes where + rnf (RangeHsSemanticTokenTypes a) = rwhnf a -instance Show GlobalTokenTypeMap where +instance Show RangeHsSemanticTokenTypes where show = const "GlobalNameMap" -type instance RuleResult GetGlobalNameSemantic = GlobalTokenTypeMap +type instance RuleResult GetSemanticTokens = RangeHsSemanticTokenTypes From b762e41ce598c1d0108c9b609e88f7e71028a140 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 29 Dec 2023 12:41:26 +0800 Subject: [PATCH 53/73] update documentation --- plugins/hls-semantic-tokens-plugin/README.md | 36 ++++++++++++------- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 26 ++++++-------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/README.md b/plugins/hls-semantic-tokens-plugin/README.md index 050a54734d..5d6be35ef5 100644 --- a/plugins/hls-semantic-tokens-plugin/README.md +++ b/plugins/hls-semantic-tokens-plugin/README.md @@ -32,23 +32,35 @@ It should be implemented in the future. * [ ] [textDocument/semanticTokens/full/delta](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#semanticTokens_deltaRequest) * [ ] [workspace/semanticTokens/refresh](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#semanticTokens_refreshRequest) -* Supported semantic tokens: +* Supported semantic tokens type: * [x] class and class method - * [x] type family name - * [x] data constructor name (not distinguishing record and normal data) - * [x] type constructor name + * [x] type family name (data family) + * [x] data constructor name (not distinguishing record and normal data, and GADT) + * [x] type constructor name (GADT) * [x] record field name * [x] type synonym * [x] pattern synonym - * [x] pattern bindings (map to parameters in standard token) - * [x] value bindings (map to functions in standard token) + * [x] ~~pattern bindings~~ In favor of differing functions and none-functions from its type + * [x] ~~value bindings~~ In favor of differing functions and none-functions from its type + * [x] functions + * [x] none-function variables * [x] imported name -* Supported modifiers: - * [ ] declaration (as in class declarations and type family) - * [ ] definition (as in class instance declaration and type family instance) - * [ ] modification (as in rec field update) +* Supported modifiers(planning): + * [future] declaration (as in class declearations, type definition and type family) + * [future] definition (as in class instance declaration, left hand side value binding, and type family instance) + * [future] modification (as in rec field update) -## Semantic highlighting sample +## Implementation details -![semantic highlighting sample](https://private-user-images.githubusercontent.com/14073857/290981908-9619fae2-cb92-4d4e-b8f8-6507851ba9f3.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MDMyOTAzMjcsIm5iZiI6MTcwMzI5MDAyNywicGF0aCI6Ii8xNDA3Mzg1Ny8yOTA5ODE5MDgtOTYxOWZhZTItY2I5Mi00ZDRlLWI4ZjgtNjUwNzg1MWJhOWYzLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFJV05KWUFYNENTVkVINTNBJTJGMjAyMzEyMjMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjMxMjIzVDAwMDcwN1omWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTNiODU1MTBhNTRhNTFhNmYzZjRjYTM1MzViZjNhMDgwYTZiODk3OTA4YzFmMTQ1ZjhhNDcyYWMyMjFlMDQwMDYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0._lgQ0lAGUD5nUkRx_s2rWxTS81ezqKr71ad2fWHGDfk) +* [x] Compute visible names from renamedsource +* [x] Compute `NameSemanticMap` for imported and top level name tokens using `HscEnv`(with deps) and type checked result +* [x] Compute current module `NameSemanticMap` using `RefMap a` from the result of `GetHieAst` +* [x] Compute all visible `(Name, Span)` in current module, in turn compute their semantic token using the combination map of the above two `NameSemanticMap` +* [x] use default legends, Candidates map of token type with default token type: [Maps to default token types](https://github.com/soulomoon/haskell-language-server/blob/master/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs) +* [x] add args support to turn the plugin on and off +* [x] enhence test +* [x] enhence error reporting. +* [x] computation of semantic tokens is pushed into rule `getSemanticTokensRule` +* [future] make use of modifiers +* [future] hadling customize legends using server capabilities (how?) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 4e843dbc87..5d84f5826d 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -68,17 +68,6 @@ logWith st prior = liftIO . logPriority (ideLogger st) prior . T.pack ---- the api ----------------------- --- | Compute semantic tokens for a Haskell source file. --- --- This function collects information from various sources, including: --- --- Imported name token type from Rule 'GetSemanticTokens' --- Local names token type from 'hieAst' --- Name locations from 'hieAst' --- Visible names from 'tmrRenamed' --- --- It then combines this information to compute the semantic tokens for the file. --- computeSemanticTokens :: IdeState -> NormalizedFilePath -> ExceptT PluginError Action SemanticTokens computeSemanticTokens state nfp = do let dbg = logWith state Debug @@ -93,10 +82,17 @@ semanticTokensFull state _ param = do items <- runActionE "SemanticTokens.semanticTokensFull" state $ computeSemanticTokens state nfp return $ InL items --- x :: Int -> Int --- x i = x i - --- | Defines the 'getSemanticTokensRule' function, which is used to record semantic tokens for imported names. +-- | Defines the 'getSemanticTokensRule' function, compute semantic tokens for a Haskell source file. +-- +-- This Rule collects information from various sources, including: +-- +-- Imported name token type from Rule 'GetSemanticTokens' +-- Local names token type from 'hieAst' +-- Name locations from 'hieAst' +-- Visible names from 'tmrRenamed' +-- +-- It then combines this information to compute the semantic tokens for the file. +-- getSemanticTokensRule :: Recorder (WithPriority Log) -> Rules () getSemanticTokensRule recorder = define (cmapWithPrio LogShake recorder) $ \GetSemanticTokens nfp -> do From e0ca926337b9c907ea84286cb2d8079f383cb56c Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 29 Dec 2023 18:32:09 +0800 Subject: [PATCH 54/73] cleanup hieAstSpanNames --- .../src/Ide/Plugin/SemanticTokens/Query.hs | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 79f6443ac1..9fc37e9296 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -87,30 +87,22 @@ hieAstSpanNames nameSet ast = then getIds ast else concatMap (hieAstSpanNames nameSet) (nodeChildren ast) where - -- getIds :: HieAST a -> [(Span, Name)] + getIds :: HieAST a -> [(Range, Name)] getIds ast' = [ (realSrcSpanToRange $ nodeSpan ast', c) - | (Right c, d) <- Map.toList $ getNodeIds' ast, - elemNameSet c nameSet, - -- at least get one info - not $ any isEvidenceBind $ identInfo d, - not $ any isEvidenceUse $ identInfo d, - Set.size (identInfo d) > 0 - -- some derived occName is visible (some fields) - -- , not $ isDerivedOccName (occName c) + | (Right c) <- S.toList $ getNodeIds' ast, + elemNameSet c nameSet ] - getNodeIds' :: HieAST a -> Map.Map Identifier (IdentifierDetails a) + getNodeIds' :: HieAST a -> S.Set Identifier getNodeIds' = - Map.foldl' combineNodeIds Map.empty + Map.foldl' combineNodeIds mempty . Map.filterWithKey (\k _ -> k == SourceInfo) . getSourcedNodeInfo . sourcedNodeInfo - - combineNodeIds :: - Map.Map Identifier (IdentifierDetails a) -> - NodeInfo a -> - Map.Map Identifier (IdentifierDetails a) - ad `combineNodeIds` (NodeInfo _ _ bd) = Map.unionWith (<>) ad bd + combineNodeIds :: S.Set Identifier -> NodeInfo a -> S.Set Identifier + ad `combineNodeIds` (NodeInfo _ _ bd) = ad `S.union` xs + where + xs = M.keysSet bd ------------------------------------------------- From 41f27a7327c1bb03904d4ee27ef3a4e87fbdf5f7 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 30 Dec 2023 02:05:27 +0800 Subject: [PATCH 55/73] remove renamed source from getSemanticTokensRule and optimize query function for semantic token type --- .../src/Ide/Plugin/SemanticTokens.hs | 2 +- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 21 ++++----- .../src/Ide/Plugin/SemanticTokens/Query.hs | 45 +++++++------------ 3 files changed, 25 insertions(+), 43 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index 34cc04393b..d9f0f262a5 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -15,7 +15,7 @@ descriptor recorder plId = (defaultPluginDescriptor plId "Provides semantic toke mkPluginHandler SMethod_TextDocumentSemanticTokensFull Internal.semanticTokensFull , Ide.Types.pluginRules = Internal.getSemanticTokensRule recorder - <> Internal.persistentSemanticMapRule + <> Internal.persistentGetSemanticTokensRule } diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 5d84f5826d..58b5e75f40 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -16,7 +16,7 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} -module Ide.Plugin.SemanticTokens.Internal (semanticTokensFull, getSemanticTokensRule, persistentSemanticMapRule) where +module Ide.Plugin.SemanticTokens.Internal (semanticTokensFull, getSemanticTokensRule, persistentGetSemanticTokensRule) where import Control.Lens ((^.)) import Control.Monad.Except (ExceptT, MonadError (..), @@ -96,29 +96,26 @@ semanticTokensFull state _ param = do getSemanticTokensRule :: Recorder (WithPriority Log) -> Rules () getSemanticTokensRule recorder = define (cmapWithPrio LogShake recorder) $ \GetSemanticTokens nfp -> do - (TcModuleResult {..}) <- use_ TypeCheck nfp (hscEnv -> hsc) <- use_ GhcSessionDeps nfp (HAR {..}) <- use_ GetHieAst nfp Just (_, ast) <- return $ listToMaybe $ Map.toList $ getAsts hieAst - let nameSet = nameGetter tmrRenamed - -- get imported name semantic map - importedNameSemanticMap <- liftIO $ foldrM (getTypeExclude (tcg_type_env tmrTypechecked) hsc) emptyNameEnv $ nameSetElemsStable nameSet -- get current location from the old ones - let spanNames = hieAstSpanNames nameSet ast + let spanNamesMap = hieAstSpanNames ast + let nameSet = unionNameSets $ Map.elems spanNamesMap + -- get imported name semantic map + importedNameSemanticMap <- liftIO $ foldrM (getTypeExclude hsc) emptyNameEnv $ nameSetElemsStable nameSet let sMap = plusNameEnv_C (<>) importedNameSemanticMap $ mkLocalNameSemanticFromAst hieKind refMap - let rangeTokenType = extractSemanticTokensFromNames sMap spanNames + let rangeTokenType = extractSemanticTokensFromNames sMap spanNamesMap return ([], Just $ RangeHsSemanticTokenTypes rangeTokenType) where -- ignore one already in current module - getTypeExclude localMap env n nameMap - | Nothing <- lookupNameEnv localMap n = + getTypeExclude env n nameMap = do tyThing <- lookupImported env n pure $ maybe nameMap (extendNameEnv nameMap n) (tyThing >>= tyThingSemantic) - | otherwise = pure nameMap lookupImported :: HscEnv -> Name -> IO (Maybe TyThing) lookupImported env = fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env -- | Persistent rule to ensure that semantic tokens doesn't block on startup -persistentSemanticMapRule :: Rules () -persistentSemanticMapRule = addPersistentRule GetSemanticTokens $ \_ -> pure $ Just (RangeHsSemanticTokenTypes mempty, idDelta, Nothing) +persistentGetSemanticTokensRule :: Rules () +persistentGetSemanticTokensRule = addPersistentRule GetSemanticTokens $ \_ -> pure $ Just (RangeHsSemanticTokenTypes mempty, idDelta, Nothing) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 9fc37e9296..c4a98fea33 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -13,10 +13,10 @@ import Data.Generics (everything) import qualified Data.List as List import qualified Data.List.NonEmpty as NE -- import HieDb.Types (DefRow (..)) +import Data.Foldable (fold) import qualified Data.Map as M import qualified Data.Map as Map -import Data.Maybe (catMaybes, listToMaybe, - mapMaybe) +import Data.Maybe (listToMaybe, mapMaybe) import qualified Data.Set as S import qualified Data.Set as Set import Data.Text (Text) @@ -68,12 +68,10 @@ nameNameSemanticFromHie hieKind rm ns = do let infos = S.unions $ map (identInfo . snd) spanInfos let typeTokenType = fmap (typeSemantic hieKind) $ listToMaybe $ mapMaybe (identType . snd) spanInfos let contextInfoTokenType = contextInfosMaybeTokenType infos - maximum <$> NE.nonEmpty (catMaybes [typeTokenType, contextInfoTokenType]) + fold [typeTokenType, contextInfoTokenType] contextInfosMaybeTokenType :: Set.Set ContextInfo -> Maybe HsSemanticTokenType - contextInfosMaybeTokenType details = case NE.nonEmpty $ Set.toList details of - Just infos -> maximum $ NE.map infoTokenType infos - Nothing -> Nothing + contextInfosMaybeTokenType details = foldMap infoTokenType (Set.toList details) ----------------------------------- @@ -81,28 +79,22 @@ nameNameSemanticFromHie hieKind rm ns = do ----------------------------------- -hieAstSpanNames :: NameSet -> HieAST a -> [(Range, Name)] -hieAstSpanNames nameSet ast = +hieAstSpanNames :: HieAST a -> M.Map Range NameSet +hieAstSpanNames ast = if null (nodeChildren ast) then getIds ast - else concatMap (hieAstSpanNames nameSet) (nodeChildren ast) + else M.unionsWith unionNameSet $ map hieAstSpanNames (nodeChildren ast) where - getIds :: HieAST a -> [(Range, Name)] - getIds ast' = - [ (realSrcSpanToRange $ nodeSpan ast', c) - | (Right c) <- S.toList $ getNodeIds' ast, - elemNameSet c nameSet - ] - getNodeIds' :: HieAST a -> S.Set Identifier + getIds ast' = M.singleton (realSrcSpanToRange $ nodeSpan ast') (getNodeIds' ast') getNodeIds' = Map.foldl' combineNodeIds mempty . Map.filterWithKey (\k _ -> k == SourceInfo) . getSourcedNodeInfo . sourcedNodeInfo - combineNodeIds :: S.Set Identifier -> NodeInfo a -> S.Set Identifier - ad `combineNodeIds` (NodeInfo _ _ bd) = ad `S.union` xs - where - xs = M.keysSet bd + combineNodeIds :: NameSet -> NodeInfo a -> NameSet + ad `combineNodeIds` (NodeInfo _ _ bd) = ad `unionNameSet` xs + where xs = mkNameSet $ rights $ M.keys bd + ------------------------------------------------- @@ -123,13 +115,6 @@ semanticTokenAbsoluteSemanticTokens = makeSemanticTokens defaultSemanticTokensLe (toLspTokenType tokenType) [] -extractSemanticTokensFromNames :: NameSemanticMap -> [(Range, Name)] -> [(Range, HsSemanticTokenType)] -extractSemanticTokensFromNames nsm = mergeNameFromSamSpan . mapMaybe (getSemantic nsm) - where - -- merge all tokens with same span - -- mergeNameFromSamSpan :: [(Span, SemanticTokenType)] -> [(Span, SemanticTokenType)] - mergeNameFromSamSpan xs = Map.toList $ Map.fromListWith (<>) xs - - getSemantic nameMap (span, name) = do - tokenType <- lookupNameEnv nameMap name - pure (span, tokenType) +extractSemanticTokensFromNames :: NameSemanticMap -> M.Map Range NameSet -> [(Range, HsSemanticTokenType)] +extractSemanticTokensFromNames nsm rnMap = xs + where xs = mapMaybe sequence (Map.toList $ Map.map (foldMap (lookupNameEnv nsm) . nameSetElemsStable) rnMap) From 1e5ddad65c4f220ab52e25b7cb4a2867e27e2a56 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 30 Dec 2023 16:56:52 +0800 Subject: [PATCH 56/73] try to exclude names that is not visible in hie and cleanup --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 52 ++++++++++--------- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 11 ++-- .../src/Ide/Plugin/SemanticTokens/Query.hs | 44 +++++++--------- .../src/Ide/Plugin/SemanticTokens/Utils.hs | 4 +- 4 files changed, 56 insertions(+), 55 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 58b5e75f40..725529f3da 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -3,7 +3,6 @@ -- This module provides the core functionality of the plugin. -- ----------------------------------------------------------------------------- - {-# LANGUAGE DataKinds #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleInstances #-} @@ -16,11 +15,13 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} +-- | +-- This module provides the core functionality of the plugin. module Ide.Plugin.SemanticTokens.Internal (semanticTokensFull, getSemanticTokensRule, persistentGetSemanticTokensRule) where import Control.Lens ((^.)) -import Control.Monad.Except (ExceptT, MonadError (..), - liftEither, withExceptT) +import Control.Monad.Except (ExceptT, liftEither, + withExceptT) import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Either (fromRight) import qualified Data.Map as Map @@ -32,15 +33,12 @@ import Development.IDE (Action, HieAstResult (HAR, hieAst, hieModule, refMap), IdeState, Priority (..), Recorder, Rules, - TypeCheck (TypeCheck), WithPriority, catchSrcErrors, cmapWithPrio, define, hieKind, ideLogger, - logPriority, - useWithStale_, use_) -import Development.IDE.Core.Compile (TcModuleResult (..), - lookupName) + logPriority, use_) +import Development.IDE.Core.Compile (lookupName) import Development.IDE.Core.PluginUtils (runActionE, useWithStaleE) import Development.IDE.Core.PositionMapping (idDelta, toCurrentRange) @@ -48,7 +46,7 @@ import Development.IDE.Core.Rules (Log (LogShake)) import Development.IDE.Core.Shake (addPersistentRule) import Development.IDE.GHC.Compat import Development.IDE.Types.HscEnvEq (hscEnv) -import Ide.Plugin.Error (PluginError (PluginInternalError, PluginRuleFailed), +import Ide.Plugin.Error (PluginError (PluginInternalError), getNormalizedFilePathE) import Ide.Plugin.SemanticTokens.Mappings import Ide.Plugin.SemanticTokens.Query @@ -70,11 +68,11 @@ logWith st prior = liftIO . logPriority (ideLogger st) prior . T.pack computeSemanticTokens :: IdeState -> NormalizedFilePath -> ExceptT PluginError Action SemanticTokens computeSemanticTokens state nfp = do - let dbg = logWith state Debug - dbg $ "Computing semantic tokens for: " <> show nfp - (RangeHsSemanticTokenTypes {tokens}, mapping) <- useWithStaleE GetSemanticTokens nfp - let rangeTokens = mapMaybe (\(span, name) -> (,name) <$> toCurrentRange mapping span) tokens - withExceptT PluginInternalError $ liftEither $ semanticTokenAbsoluteSemanticTokens rangeTokens + let dbg = logWith state Debug + dbg $ "Computing semantic tokens for: " <> show nfp + (RangeHsSemanticTokenTypes {tokens}, mapping) <- useWithStaleE GetSemanticTokens nfp + let rangeTokens = mapMaybe (\(span, name) -> (,name) <$> toCurrentRange mapping span) tokens + withExceptT PluginInternalError $ liftEither $ semanticTokenAbsoluteSemanticTokens rangeTokens semanticTokensFull :: PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull semanticTokensFull state _ param = do @@ -92,7 +90,6 @@ semanticTokensFull state _ param = do -- Visible names from 'tmrRenamed' -- -- It then combines this information to compute the semantic tokens for the file. --- getSemanticTokensRule :: Recorder (WithPriority Log) -> Rules () getSemanticTokensRule recorder = define (cmapWithPrio LogShake recorder) $ \GetSemanticTokens nfp -> do @@ -102,19 +99,24 @@ getSemanticTokensRule recorder = -- get current location from the old ones let spanNamesMap = hieAstSpanNames ast let nameSet = unionNameSets $ Map.elems spanNamesMap + let localSemanticMap = mkLocalNameSemanticFromAst nameSet hieKind refMap -- get imported name semantic map - importedNameSemanticMap <- liftIO $ foldrM (getTypeExclude hsc) emptyNameEnv $ nameSetElemsStable nameSet - let sMap = plusNameEnv_C (<>) importedNameSemanticMap $ mkLocalNameSemanticFromAst hieKind refMap + -- liftIO $ putStrLn $ unlines $ fmap showClearName $ nameSetElemsStable nameSet + importedNameSemanticMap <- liftIO $ foldrM (getTypeExclude localSemanticMap hsc) emptyNameEnv $ nameSetElemsStable nameSet + -- let importedNameSemanticMap = computeImportedNameSemanticMap $ nameSetElemsStable nameSet + let sMap = plusNameEnv_C (<>) importedNameSemanticMap localSemanticMap let rangeTokenType = extractSemanticTokensFromNames sMap spanNamesMap return ([], Just $ RangeHsSemanticTokenTypes rangeTokenType) - where - -- ignore one already in current module - getTypeExclude env n nameMap = - do tyThing <- lookupImported env n - pure $ maybe nameMap (extendNameEnv nameMap n) (tyThing >>= tyThingSemantic) - lookupImported :: HscEnv -> Name -> IO (Maybe TyThing) - lookupImported env = fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env - + where + -- ignore one already in discovered in local + getTypeExclude localEnv env n nameMap + | n `elemNameEnv` localEnv = pure nameMap + | otherwise = + do + tyThing <- lookupImported env n + pure $ maybe nameMap (extendNameEnv nameMap n) (tyThing >>= tyThingSemantic) + lookupImported :: HscEnv -> Name -> IO (Maybe TyThing) + lookupImported env = fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env -- | Persistent rule to ensure that semantic tokens doesn't block on startup persistentGetSemanticTokensRule :: Rules () diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index dc256c70f2..755ee08c9b 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -89,15 +89,18 @@ tyThingSemantic ty = case ty of isFunType :: Type -> Bool isFunType a = case a of ForAllTy _ t -> isFunType t +-- Development.IDE.GHC.Compat.Core.FunTy(pattern synonym) hides FunTyFlag which is used to distinguish +-- (->, =>, etc..) +-- FunTy _ _ -> True _x -> isFunTy a -typeSemantic :: HieKind hType -> hType -> HsSemanticTokenType +typeSemantic :: HieKind hType -> hType -> Maybe HsSemanticTokenType typeSemantic kind t = case kind of - HieFresh -> if isFunType t then TFunction else TVariable + HieFresh -> if isFunType t then Just TFunction else Nothing HieFromDisk full_file -> if isFixFunction fullType - then TFunction - else TVariable + then Just TFunction + else Nothing where fullType = recoverFullType t (hie_types full_file) where diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index c4a98fea33..9776d024d9 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -4,16 +4,14 @@ {-# LANGUAGE ExplicitNamespaces #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TupleSections #-} + module Ide.Plugin.SemanticTokens.Query where import Data.Either (rights) +import Data.Foldable (fold) import Data.Generics (everything) import qualified Data.List as List -import qualified Data.List.NonEmpty as NE --- import HieDb.Types (DefRow (..)) -import Data.Foldable (fold) import qualified Data.Map as M import qualified Data.Map as Map import Data.Maybe (listToMaybe, mapMaybe) @@ -30,28 +28,14 @@ import Ide.Plugin.SemanticTokens.Types (HsSemanticTokenType, import Language.LSP.Protocol.Types import Prelude hiding (span) ------------------------------------------------- - --- * extract names from RenamedSource - ------------------------------------------------- - -nameToCollect :: Name -> NameSet -nameToCollect = unitNameSet - -nameGetter :: RenamedSource -> NameSet -nameGetter = everything unionNameSet (emptyNameSet `mkQ` nameToCollect) - --------------------------------------------------------- -- * extract semantic map from HieAst for local variables --------------------------------------------------------- -mkLocalNameSemanticFromAst :: HieKind a -> RefMap a -> NameSemanticMap -mkLocalNameSemanticFromAst hieKind rm = mkNameEnv (mapMaybe (nameNameSemanticFromHie hieKind rm) ns) - where - ns = rights $ M.keys rm +mkLocalNameSemanticFromAst :: NameSet -> HieKind a -> RefMap a -> NameSemanticMap +mkLocalNameSemanticFromAst nameSet hieKind rm = mkNameEnv (mapMaybe (nameNameSemanticFromHie hieKind rm) $ nameSetElemsStable nameSet) nameNameSemanticFromHie :: forall a. HieKind a -> RefMap a -> Name -> Maybe (Name, HsSemanticTokenType) nameNameSemanticFromHie hieKind rm ns = do @@ -66,9 +50,9 @@ nameNameSemanticFromHie hieKind rm ns = do spanInfos <- -- traceShow ("getting spans:", nameString) $ Map.lookup (Right name') rm' let infos = S.unions $ map (identInfo . snd) spanInfos - let typeTokenType = fmap (typeSemantic hieKind) $ listToMaybe $ mapMaybe (identType . snd) spanInfos - let contextInfoTokenType = contextInfosMaybeTokenType infos - fold [typeTokenType, contextInfoTokenType] + let typeTokenType = foldMap (typeSemantic hieKind) $ listToMaybe $ mapMaybe (identType . snd) spanInfos + contextInfoTokenType <- contextInfosMaybeTokenType infos + fold [typeTokenType, Just contextInfoTokenType] contextInfosMaybeTokenType :: Set.Set ContextInfo -> Maybe HsSemanticTokenType contextInfosMaybeTokenType details = foldMap infoTokenType (Set.toList details) @@ -79,6 +63,9 @@ nameNameSemanticFromHie hieKind rm ns = do ----------------------------------- +-- | get only visible names from HieAST +-- we care only the leaf node of the AST +-- and filter out the derived and evidence names hieAstSpanNames :: HieAST a -> M.Map Range NameSet hieAstSpanNames ast = if null (nodeChildren ast) @@ -93,7 +80,16 @@ hieAstSpanNames ast = . sourcedNodeInfo combineNodeIds :: NameSet -> NodeInfo a -> NameSet ad `combineNodeIds` (NodeInfo _ _ bd) = ad `unionNameSet` xs - where xs = mkNameSet $ rights $ M.keys bd + where + xs = mkNameSet $ rights $ M.keys $ M.filterWithKey inclusion bd + inclusion :: Identifier -> IdentifierDetails a -> Bool + inclusion a b = not $ exclusion a b + exclusion :: Identifier -> IdentifierDetails a -> Bool + exclusion idt IdentifierDetails {identInfo = infos} = case idt of + Left _ -> True + Right name -> + isDerivedOccName (nameOccName name) + || any isEvidenceContext (S.toList infos) ------------------------------------------------- diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs index 971069d1e8..62b376d636 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs @@ -62,10 +62,10 @@ showName name = showSDocUnsafe (ppr name) <> ":" <> showNameType name showNameType :: Name -> String showNameType name + | isWiredInName name = "WiredInName" + | isSystemName name = "SystemName" | isInternalName name = "InternalName" | isExternalName name = "ExternalName" - | isSystemName name = "SystemName" - | isWiredInName name = "WiredInName" | otherwise = "UnknownName" bytestringString :: ByteString -> String From e5b42ac9fe4d1337ebd6d9ef1ec8487017758e33 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 30 Dec 2023 18:51:36 +0800 Subject: [PATCH 57/73] add HieFunMaskKind, it is to differ wether a type at type index is a function or non-function --- .../hls-semantic-tokens-plugin.cabal | 1 + .../src/Ide/Plugin/SemanticTokens/Internal.hs | 6 ++-- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 27 ++++++++---------- .../src/Ide/Plugin/SemanticTokens/Query.hs | 12 ++++---- .../src/Ide/Plugin/SemanticTokens/Types.hs | 6 ++++ .../src/Ide/Plugin/SemanticTokens/Utils.hs | 28 +++++++++++++++++++ 6 files changed, 55 insertions(+), 25 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal index 488de75320..4bec95ff42 100644 --- a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal +++ b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal @@ -49,6 +49,7 @@ library , transformers , bytestring , syb + , array , deepseq , hls-graph == 2.5.0.0 diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 725529f3da..e6ad1a4b9e 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -98,11 +98,11 @@ getSemanticTokensRule recorder = Just (_, ast) <- return $ listToMaybe $ Map.toList $ getAsts hieAst -- get current location from the old ones let spanNamesMap = hieAstSpanNames ast - let nameSet = unionNameSets $ Map.elems spanNamesMap - let localSemanticMap = mkLocalNameSemanticFromAst nameSet hieKind refMap + let names = nameSetElemsStable $ unionNameSets $ Map.elems spanNamesMap + let localSemanticMap = mkLocalNameSemanticFromAst names (hieKindFunMasksKind hieKind) refMap -- get imported name semantic map -- liftIO $ putStrLn $ unlines $ fmap showClearName $ nameSetElemsStable nameSet - importedNameSemanticMap <- liftIO $ foldrM (getTypeExclude localSemanticMap hsc) emptyNameEnv $ nameSetElemsStable nameSet + importedNameSemanticMap <- liftIO $ foldrM (getTypeExclude localSemanticMap hsc) emptyNameEnv names -- let importedNameSemanticMap = computeImportedNameSemanticMap $ nameSetElemsStable nameSet let sMap = plusNameEnv_C (<>) importedNameSemanticMap localSemanticMap let rangeTokenType = extractSemanticTokensFromNames sMap spanNamesMap diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 755ee08c9b..4f6afe3ced 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -12,6 +12,7 @@ -- 4. Mapping from LSP tokens to SemanticTokenOriginal. module Ide.Plugin.SemanticTokens.Mappings where +import qualified Data.Array as A import Data.List.Extra (chunksOf, (!?)) import qualified Data.Map as Map import qualified Data.Set as Set @@ -19,6 +20,7 @@ import Data.Text (Text) import Development.IDE (HieKind (HieFresh, HieFromDisk)) import Development.IDE.GHC.Compat import Ide.Plugin.SemanticTokens.Types +import Ide.Plugin.SemanticTokens.Utils (recoverFunMaskArray) import Language.LSP.Protocol.Types (LspEnum (knownValues), SemanticTokenAbsolute (SemanticTokenAbsolute), SemanticTokenRelative (SemanticTokenRelative), @@ -91,25 +93,18 @@ isFunType a = case a of ForAllTy _ t -> isFunType t -- Development.IDE.GHC.Compat.Core.FunTy(pattern synonym) hides FunTyFlag which is used to distinguish -- (->, =>, etc..) --- FunTy _ _ -> True + FunTy _ _ -> True _x -> isFunTy a -typeSemantic :: HieKind hType -> hType -> Maybe HsSemanticTokenType +hieKindFunMasksKind :: HieKind a -> HieFunMaskKind a +hieKindFunMasksKind hieKind = case hieKind of + HieFresh -> HieFreshFun + HieFromDisk full_file -> HieFromDiskFun $ recoverFunMaskArray (hie_types full_file) + +typeSemantic :: HieFunMaskKind hType -> hType -> Maybe HsSemanticTokenType typeSemantic kind t = case kind of - HieFresh -> if isFunType t then Just TFunction else Nothing - HieFromDisk full_file -> - if isFixFunction fullType - then Just TFunction - else Nothing - where - fullType = recoverFullType t (hie_types full_file) - where - isFixFunction :: HieTypeFix -> Bool - isFixFunction (Roll x) = - case x of - HForAllTy _ a -> isFixFunction a - HFunTy {} -> True - _ -> False + HieFreshFun -> if isFunType t then Just TFunction else Nothing + HieFromDiskFun arr -> if arr A.! t then Just TFunction else Nothing -- * 3. Mapping from hieAst ContextInfo to haskell semantic token type. diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 9776d024d9..f1a9077cbe 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -23,7 +23,8 @@ import Development.IDE (HieKind, import Development.IDE.GHC.Compat import Generics.SYB (mkQ) import Ide.Plugin.SemanticTokens.Mappings -import Ide.Plugin.SemanticTokens.Types (HsSemanticTokenType, +import Ide.Plugin.SemanticTokens.Types (HieFunMaskKind, + HsSemanticTokenType, NameSemanticMap) import Language.LSP.Protocol.Types import Prelude hiding (span) @@ -34,10 +35,10 @@ import Prelude hiding (span) --------------------------------------------------------- -mkLocalNameSemanticFromAst :: NameSet -> HieKind a -> RefMap a -> NameSemanticMap -mkLocalNameSemanticFromAst nameSet hieKind rm = mkNameEnv (mapMaybe (nameNameSemanticFromHie hieKind rm) $ nameSetElemsStable nameSet) +mkLocalNameSemanticFromAst :: [Name] -> HieFunMaskKind a -> RefMap a -> NameSemanticMap +mkLocalNameSemanticFromAst names hieKind rm = mkNameEnv (mapMaybe (nameNameSemanticFromHie hieKind rm) names) -nameNameSemanticFromHie :: forall a. HieKind a -> RefMap a -> Name -> Maybe (Name, HsSemanticTokenType) +nameNameSemanticFromHie :: forall a. HieFunMaskKind a -> RefMap a -> Name -> Maybe (Name, HsSemanticTokenType) nameNameSemanticFromHie hieKind rm ns = do st <- -- traceShow ("to find Name", showName ns) $ nameSemanticFromRefMap rm ns @@ -49,9 +50,8 @@ nameNameSemanticFromHie hieKind rm ns = do nameSemanticFromRefMap rm' name' = do spanInfos <- -- traceShow ("getting spans:", nameString) $ Map.lookup (Right name') rm' - let infos = S.unions $ map (identInfo . snd) spanInfos let typeTokenType = foldMap (typeSemantic hieKind) $ listToMaybe $ mapMaybe (identType . snd) spanInfos - contextInfoTokenType <- contextInfosMaybeTokenType infos + contextInfoTokenType <- foldMap (contextInfosMaybeTokenType . identInfo . snd) spanInfos fold [typeTokenType, Just contextInfoTokenType] contextInfosMaybeTokenType :: Set.Set ContextInfo -> Maybe HsSemanticTokenType diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 0b8a1ba0ba..4854790dfa 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -1,10 +1,12 @@ {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE StrictData #-} {-# LANGUAGE TypeFamilies #-} module Ide.Plugin.SemanticTokens.Types where import Control.DeepSeq (NFData (rnf), rwhnf) +import qualified Data.Array as A import Data.Generics (Typeable) import Development.IDE (RuleResult) import Development.IDE.GHC.Compat hiding (loc) @@ -72,3 +74,7 @@ instance Show RangeHsSemanticTokenTypes where show = const "GlobalNameMap" type instance RuleResult GetSemanticTokens = RangeHsSemanticTokenTypes + +data HieFunMaskKind kind where + HieFreshFun :: HieFunMaskKind Type + HieFromDiskFun :: A.Array TypeIndex Bool -> HieFunMaskKind TypeIndex diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs index 62b376d636..556720d79e 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs @@ -5,6 +5,7 @@ module Ide.Plugin.SemanticTokens.Utils where +import qualified Data.Array as A import Data.ByteString (ByteString) import Data.ByteString.Char8 (unpack) import qualified Data.Map as Map @@ -92,3 +93,30 @@ nameMapString nsm names = unlines showSpan :: RealSrcSpan -> String showSpan x = show (srcSpanStartLine x) <> ":" <> show (srcSpanStartCol x) <> "-" <> show (srcSpanEndCol x) + + +-- idea from https://gitlab.haskell.org/ghc/haddock/-/blob/b0b0e0366457c9aefebcc94df74e5de4d00e17b7/haddock-api/src/Haddock/Backends/Hyperlinker/Utils.hs#L107 +recoverFunMaskArray + :: A.Array TypeIndex HieTypeFlat -- ^ flat types + -> A.Array TypeIndex Bool-- ^ full AST +recoverFunMaskArray flattened = unflattened + where + -- The recursion in 'unflattened' is crucial - it's what gives us sharing + -- between the IfaceType's produced + unflattened :: A.Array TypeIndex Bool + unflattened = fmap (\flatTy -> go (fmap (unflattened A.!) flatTy)) flattened + + -- Unfold an 'HieType' whose subterms have already been unfolded + go :: HieType Bool -> Bool + go (HTyVarTy _name) = False + go (HAppTy _f _x) = False + go (HLitTy _lit) = False + go (HForAllTy ((_n,_k),_af) b) = b + go (HFunTy _ _ _) = True + go (HQualTy _constraint b) = b + go (HCastTy b) = b + go HCoercionTy = False + go (HTyConApp _ _) = False + + + From 193b15a6b21903471a9158d450fca92e54fde827 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 30 Dec 2023 19:25:44 +0800 Subject: [PATCH 58/73] expose function flag to expose (=>, ->, -=>, ==>) --- ghcide/src/Development/IDE/GHC/Compat/Core.hs | 14 +++++++------- ghcide/src/Development/IDE/Spans/AtPoint.hs | 2 +- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 10 +++++----- plugins/hls-semantic-tokens-plugin/test/Main.hs | 1 + .../test/testdata/TFunction.expected | 2 +- .../testdata/TInstanceClassMethodBind.expected | 2 +- .../testdata/TNoneFunctionWithConstraint.expected | 6 ++++++ .../test/testdata/TNoneFunctionWithConstraint.hs | 5 +++++ 8 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TNoneFunctionWithConstraint.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TNoneFunctionWithConstraint.hs diff --git a/ghcide/src/Development/IDE/GHC/Compat/Core.hs b/ghcide/src/Development/IDE/GHC/Compat/Core.hs index bb57f602b7..af492c9b6d 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Core.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Core.hs @@ -431,13 +431,13 @@ import GHC.Core.DataCon hiding (dataConExTyCoVars) import qualified GHC.Core.DataCon as DataCon import GHC.Core.FamInstEnv hiding (pprFamInst) import GHC.Core.InstEnv -import GHC.Types.Unique.FM +import GHC.Types.Unique.FM import GHC.Core.PatSyn import GHC.Core.Predicate import GHC.Core.TyCo.Ppr import qualified GHC.Core.TyCo.Rep as TyCoRep import GHC.Core.TyCon -import GHC.Core.Type +import GHC.Core.Type import GHC.Core.Unify import GHC.Core.Utils import GHC.Driver.CmdLine (Warn (..)) @@ -597,7 +597,7 @@ pattern RealSrcLoc x y = SrcLoc.RealSrcLoc x y pattern AvailTC :: Name -> [Name] -> [FieldLabel] -> Avail.AvailInfo #if __GLASGOW_HASKELL__ >= 907 pattern AvailTC n names pieces <- Avail.AvailTC n ((,[]) -> (names,pieces)) -#else +#else pattern AvailTC n names pieces <- Avail.AvailTC n ((\gres -> foldr (\gre (names, pieces) -> case gre of Avail.NormalGreName name -> (name: names, pieces) Avail.FieldGreName label -> (names, label:pieces)) ([], []) gres) -> (names, pieces)) @@ -606,14 +606,14 @@ pattern AvailTC n names pieces <- Avail.AvailTC n ((\gres -> foldr (\gre (names, pattern AvailName :: Name -> Avail.AvailInfo #if __GLASGOW_HASKELL__ >= 907 pattern AvailName n <- Avail.Avail n -#else +#else pattern AvailName n <- Avail.Avail (Avail.NormalGreName n) #endif pattern AvailFL :: FieldLabel -> Avail.AvailInfo #if __GLASGOW_HASKELL__ >= 907 pattern AvailFL fl <- (const Nothing -> Just fl) -- this pattern always fails as this field was removed in 9.7 -#else +#else pattern AvailFL fl <- Avail.Avail (Avail.FieldGreName fl) #endif @@ -630,8 +630,8 @@ pattern ExposePackage s a mr <- DynFlags.ExposePackage s a _ mr pattern ExposePackage s a mr = DynFlags.ExposePackage s a mr #endif -pattern FunTy :: Type -> Type -> Type -pattern FunTy arg res <- TyCoRep.FunTy {ft_arg = arg, ft_res = res} +pattern FunTy :: FunTyFlag -> Type -> Type -> Type +pattern FunTy af arg res <- TyCoRep.FunTy {ft_af = af, ft_arg = arg, ft_res = res} -- type HasSrcSpan x a = (GenLocated SrcSpan a ~ x) -- type HasSrcSpan x = () :: Constraint diff --git a/ghcide/src/Development/IDE/Spans/AtPoint.hs b/ghcide/src/Development/IDE/Spans/AtPoint.hs index 5f1c68b83b..00f62e82e4 100644 --- a/ghcide/src/Development/IDE/Spans/AtPoint.hs +++ b/ghcide/src/Development/IDE/Spans/AtPoint.hs @@ -346,7 +346,7 @@ namesInType (TyVarTy n) = [varName n] namesInType (AppTy a b) = getTypes [a,b] namesInType (TyConApp tc ts) = tyConName tc : getTypes ts namesInType (ForAllTy b t) = varName (binderVar b) : namesInType t -namesInType (FunTy a b) = getTypes [a,b] +namesInType (FunTy _ a b) = getTypes [a,b] namesInType (CastTy t _) = namesInType t namesInType (LitTy _) = [] namesInType _ = [] diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 4f6afe3ced..a438e458b2 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -53,7 +53,6 @@ toLspTokenType tk = case tk of -- not sure if this is correct choice TTypeFamily -> SemanticTokenTypes_Interface --- TNothing -> Nothing lspTokenReverseMap :: Map.Map SemanticTokenTypes HsSemanticTokenType lspTokenReverseMap = Map.fromList $ map (\x -> (toLspTokenType x, x)) $ enumFrom minBound @@ -90,11 +89,12 @@ tyThingSemantic ty = case ty of isFunType :: Type -> Bool isFunType a = case a of - ForAllTy _ t -> isFunType t --- Development.IDE.GHC.Compat.Core.FunTy(pattern synonym) hides FunTyFlag which is used to distinguish + ForAllTy _ t -> isFunType t +-- Development.IDE.GHC.Compat.Core.FunTy(pattern synonym), FunTyFlag which is used to distinguish -- (->, =>, etc..) - FunTy _ _ -> True - _x -> isFunTy a + FunTy FTF_T_T _ _ -> True + FunTy _ _ rhs -> isFunType rhs + _x -> isFunTy a hieKindFunMasksKind :: HieKind a -> HieFunMaskKind a hieKindFunMasksKind hieKind = case hieKind of diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 8e49c413b1..f0e241c0ca 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -179,6 +179,7 @@ semanticTokensFunctionTests = [ goldenWithSemanticTokens "functions" "TFunction", goldenWithSemanticTokens "local functions" "TFunctionLocal", goldenWithSemanticTokens "function in let binding" "TFunctionLet" + , goldenWithSemanticTokens "negative case non-function with constraint" "TNoneFunctionWithConstraint" ] main :: IO () diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected index 701247bd5f..f34510728b 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TFunction.expected @@ -8,4 +8,4 @@ 6:1-2 TVariable "x" 6:6-7 TTypeVariable "a" 7:1-2 TVariable "x" -7:5-14 TFunction "undefined" +7:5-14 TVariable "undefined" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected index e9615920a2..d0cfc85d3b 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TInstanceClassMethodBind.expected @@ -4,4 +4,4 @@ 5:10-12 TClass "Eq" 5:13-16 TTypeCon "Foo" 6:5-9 TClassMethod "(==)" -6:12-21 TFunction "undefined" +6:12-21 TVariable "undefined" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TNoneFunctionWithConstraint.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TNoneFunctionWithConstraint.expected new file mode 100644 index 0000000000..2dd89fd1da --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TNoneFunctionWithConstraint.expected @@ -0,0 +1,6 @@ +3:1-2 TVariable "x" +3:7-9 TClass "Eq" +3:10-11 TTypeVariable "a" +3:16-17 TTypeVariable "a" +4:1-2 TVariable "x" +4:5-14 TVariable "undefined" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TNoneFunctionWithConstraint.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TNoneFunctionWithConstraint.hs new file mode 100644 index 0000000000..9a7119dbdb --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TNoneFunctionWithConstraint.hs @@ -0,0 +1,5 @@ +module TNoneFunctionWithConstraint where + +x :: (Eq a) => a +x = undefined + From dc8fd6d93af2bc69d32c7482640b9e309d36fc01 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 30 Dec 2023 22:57:51 +0800 Subject: [PATCH 59/73] 1. Relax GetDocMap kindMap to get TyThing for more than type variables. 2. Backport isVisibleFunArg --- ghcide/src/Development/IDE/GHC/Compat/Core.hs | 14 +++++++++- .../Development/IDE/Spans/Documentation.hs | 3 +- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 28 +++++++++---------- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 7 ++--- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/ghcide/src/Development/IDE/GHC/Compat/Core.hs b/ghcide/src/Development/IDE/GHC/Compat/Core.hs index af492c9b6d..caee9d5685 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Core.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Core.hs @@ -407,6 +407,7 @@ module Development.IDE.GHC.Compat.Core ( field_label, #endif groupOrigin, + isVisibleFunArg, ) where import qualified GHC @@ -489,6 +490,8 @@ import qualified GHC.Types.SrcLoc as SrcLoc import GHC.Types.Unique.Supply import GHC.Types.Var (Var (varName), setTyVarUnique, setVarUnique) + +import qualified GHC.Types.Var as TypesVar import GHC.Unit.Info (PackageName (..)) import GHC.Unit.Module hiding (ModLocation (..), UnitId, moduleUnit, @@ -630,9 +633,18 @@ pattern ExposePackage s a mr <- DynFlags.ExposePackage s a _ mr pattern ExposePackage s a mr = DynFlags.ExposePackage s a mr #endif -pattern FunTy :: FunTyFlag -> Type -> Type -> Type +#if __GLASGOW_HASKELL__ >= 906 +isVisibleFunArg = TypesVar.isVisibleFunArg +type FunTyFlag = TypesVar.FunTyFlag +#else +isVisibleFunArg VisArg = True +isVisibleFunArg _ = False +type FunTyFlag = TypesVar.AnonArgFlag +#endif +pattern FunTy :: Development.IDE.GHC.Compat.Core.FunTyFlag -> Type -> Type -> Type pattern FunTy af arg res <- TyCoRep.FunTy {ft_af = af, ft_arg = arg, ft_res = res} + -- type HasSrcSpan x a = (GenLocated SrcSpan a ~ x) -- type HasSrcSpan x = () :: Constraint diff --git a/ghcide/src/Development/IDE/Spans/Documentation.hs b/ghcide/src/Development/IDE/Spans/Documentation.hs index 7f74b936a0..e744791bcf 100644 --- a/ghcide/src/Development/IDE/Spans/Documentation.hs +++ b/ghcide/src/Development/IDE/Spans/Documentation.hs @@ -61,8 +61,7 @@ mkDocMap env rm this_mod = doc <- getDocumentationTryGhc env n pure $ extendNameEnv nameMap n doc getType n nameMap - | isTcOcc $ occName n - , Nothing <- lookupNameEnv nameMap n + | Nothing <- lookupNameEnv nameMap n = do kind <- lookupKind env n pure $ maybe nameMap (extendNameEnv nameMap n) kind | otherwise = pure nameMap diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index e6ad1a4b9e..77b9d87319 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -13,7 +13,6 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE ViewPatterns #-} -- | -- This module provides the core functionality of the plugin. @@ -28,24 +27,22 @@ import qualified Data.Map as Map import Data.Maybe (listToMaybe, mapMaybe) import qualified Data.Text as T import Development.IDE (Action, + GetDocMap (GetDocMap), GetHieAst (GetHieAst), - GhcSessionDeps (GhcSessionDeps), HieAstResult (HAR, hieAst, hieModule, refMap), IdeState, Priority (..), Recorder, Rules, WithPriority, - catchSrcErrors, cmapWithPrio, define, hieKind, ideLogger, logPriority, use_) -import Development.IDE.Core.Compile (lookupName) import Development.IDE.Core.PluginUtils (runActionE, useWithStaleE) import Development.IDE.Core.PositionMapping (idDelta, toCurrentRange) import Development.IDE.Core.Rules (Log (LogShake)) +import Development.IDE.Core.RuleTypes (DocAndKindMap (..)) import Development.IDE.Core.Shake (addPersistentRule) import Development.IDE.GHC.Compat -import Development.IDE.Types.HscEnvEq (hscEnv) import Ide.Plugin.Error (PluginError (PluginInternalError), getNormalizedFilePathE) import Ide.Plugin.SemanticTokens.Mappings @@ -93,8 +90,9 @@ semanticTokensFull state _ param = do getSemanticTokensRule :: Recorder (WithPriority Log) -> Rules () getSemanticTokensRule recorder = define (cmapWithPrio LogShake recorder) $ \GetSemanticTokens nfp -> do - (hscEnv -> hsc) <- use_ GhcSessionDeps nfp + -- (hscEnv -> hsc) <- use_ GhcSessionDeps nfp (HAR {..}) <- use_ GetHieAst nfp + (DKMap{getKindMap}) <- use_ GetDocMap nfp Just (_, ast) <- return $ listToMaybe $ Map.toList $ getAsts hieAst -- get current location from the old ones let spanNamesMap = hieAstSpanNames ast @@ -102,21 +100,23 @@ getSemanticTokensRule recorder = let localSemanticMap = mkLocalNameSemanticFromAst names (hieKindFunMasksKind hieKind) refMap -- get imported name semantic map -- liftIO $ putStrLn $ unlines $ fmap showClearName $ nameSetElemsStable nameSet - importedNameSemanticMap <- liftIO $ foldrM (getTypeExclude localSemanticMap hsc) emptyNameEnv names + let importedNameSemanticMap = foldr (getTypeExclude localSemanticMap getKindMap) emptyNameEnv names -- let importedNameSemanticMap = computeImportedNameSemanticMap $ nameSetElemsStable nameSet let sMap = plusNameEnv_C (<>) importedNameSemanticMap localSemanticMap let rangeTokenType = extractSemanticTokensFromNames sMap spanNamesMap return ([], Just $ RangeHsSemanticTokenTypes rangeTokenType) where -- ignore one already in discovered in local - getTypeExclude localEnv env n nameMap - | n `elemNameEnv` localEnv = pure nameMap + getTypeExclude :: NameEnv a + -> NameEnv TyThing + -> Name + -> NameEnv HsSemanticTokenType + -> NameEnv HsSemanticTokenType + getTypeExclude localEnv kindMap n nameMap + | n `elemNameEnv` localEnv = nameMap | otherwise = - do - tyThing <- lookupImported env n - pure $ maybe nameMap (extendNameEnv nameMap n) (tyThing >>= tyThingSemantic) - lookupImported :: HscEnv -> Name -> IO (Maybe TyThing) - lookupImported env = fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env + let tyThing = lookupNameEnv kindMap n in + maybe nameMap (extendNameEnv nameMap n) (tyThing >>= tyThingSemantic) -- | Persistent rule to ensure that semantic tokens doesn't block on startup persistentGetSemanticTokensRule :: Rules () diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index a438e458b2..4ca49f6bb9 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -89,12 +89,11 @@ tyThingSemantic ty = case ty of isFunType :: Type -> Bool isFunType a = case a of - ForAllTy _ t -> isFunType t + ForAllTy _ t -> isFunType t -- Development.IDE.GHC.Compat.Core.FunTy(pattern synonym), FunTyFlag which is used to distinguish -- (->, =>, etc..) - FunTy FTF_T_T _ _ -> True - FunTy _ _ rhs -> isFunType rhs - _x -> isFunTy a + FunTy flg _ rhs-> if isVisibleFunArg flg then True else isFunType rhs + _x -> isFunTy a hieKindFunMasksKind :: HieKind a -> HieFunMaskKind a hieKindFunMasksKind hieKind = case hieKind of From 6f9a903da4d1c313e92c460247d367afe3d50509 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sun, 31 Dec 2023 00:20:19 +0800 Subject: [PATCH 60/73] use customize logger, add test for unicode --- .../src/Ide/Plugin/SemanticTokens.hs | 6 +-- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 52 +++++++++++++------ .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 2 +- .../src/Ide/Plugin/SemanticTokens/Types.hs | 22 ++++++-- .../hls-semantic-tokens-plugin/test/Main.hs | 5 +- .../test/testdata/TUnicodeSyntax.expected | 1 + .../test/testdata/TUnicodeSyntax.hs | 5 ++ 7 files changed, 64 insertions(+), 29 deletions(-) create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TUnicodeSyntax.expected create mode 100644 plugins/hls-semantic-tokens-plugin/test/testdata/TUnicodeSyntax.hs diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index d9f0f262a5..34b4d480cb 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -2,14 +2,14 @@ module Ide.Plugin.SemanticTokens (descriptor) where import Development.IDE -import Development.IDE.Core.Rules (Log) import qualified Ide.Plugin.SemanticTokens.Internal as Internal +import Ide.Plugin.SemanticTokens.Types import Ide.Types import Language.LSP.Protocol.Message -descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState +descriptor :: Recorder (WithPriority SemanticLog) -> PluginId -> PluginDescriptor IdeState descriptor recorder plId = (defaultPluginDescriptor plId "Provides semantic tokens") { Ide.Types.pluginHandlers = mkPluginHandler SMethod_TextDocumentSemanticTokensFull Internal.semanticTokensFull @@ -17,5 +17,3 @@ descriptor recorder plId = (defaultPluginDescriptor plId "Provides semantic toke Internal.getSemanticTokensRule recorder <> Internal.persistentGetSemanticTokensRule } - - diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 77b9d87319..bb8d4971ab 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -13,6 +13,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UnicodeSyntax #-} -- | -- This module provides the core functionality of the plugin. @@ -22,6 +23,8 @@ import Control.Lens ((^.)) import Control.Monad.Except (ExceptT, liftEither, withExceptT) import Control.Monad.IO.Class (MonadIO, liftIO) +import Control.Monad.Trans (lift) +import Control.Monad.Trans.Except (runExceptT) import Data.Either (fromRight) import qualified Data.Map as Map import Data.Maybe (listToMaybe, mapMaybe) @@ -30,21 +33,25 @@ import Development.IDE (Action, GetDocMap (GetDocMap), GetHieAst (GetHieAst), HieAstResult (HAR, hieAst, hieModule, refMap), - IdeState, Priority (..), - Recorder, Rules, - WithPriority, + IdeResult, IdeState, + Pretty (pretty), + Priority (..), Recorder, + Rules, WithPriority, cmapWithPrio, define, hieKind, ideLogger, logPriority, use_) import Development.IDE.Core.PluginUtils (runActionE, useWithStaleE) import Development.IDE.Core.PositionMapping (idDelta, toCurrentRange) -import Development.IDE.Core.Rules (Log (LogShake)) +import Development.IDE.Core.Rules (toIdeResult) import Development.IDE.Core.RuleTypes (DocAndKindMap (..)) -import Development.IDE.Core.Shake (addPersistentRule) -import Development.IDE.GHC.Compat +import Development.IDE.Core.Shake (addPersistentRule, + useWithStale_) +import Development.IDE.GHC.Compat hiding (Warning) +import Ide.Logger (logWith) import Ide.Plugin.Error (PluginError (PluginInternalError), - getNormalizedFilePathE) + getNormalizedFilePathE, + handleMaybe) import Ide.Plugin.SemanticTokens.Mappings import Ide.Plugin.SemanticTokens.Query import Ide.Plugin.SemanticTokens.Types @@ -56,8 +63,8 @@ import Language.LSP.Protocol.Types (NormalizedFilePath, type (|?) (InL)) import Prelude hiding (span) -logWith :: (MonadIO m) => IdeState -> Priority -> String -> m () -logWith st prior = liftIO . logPriority (ideLogger st) prior . T.pack +logActionWith :: (MonadIO m) => IdeState -> Priority -> String -> m () +logActionWith st prior = liftIO . logPriority (ideLogger st) prior . T.pack ----------------------- ---- the api @@ -65,7 +72,7 @@ logWith st prior = liftIO . logPriority (ideLogger st) prior . T.pack computeSemanticTokens :: IdeState -> NormalizedFilePath -> ExceptT PluginError Action SemanticTokens computeSemanticTokens state nfp = do - let dbg = logWith state Debug + let dbg = logActionWith state Debug dbg $ "Computing semantic tokens for: " <> show nfp (RangeHsSemanticTokenTypes {tokens}, mapping) <- useWithStaleE GetSemanticTokens nfp let rangeTokens = mapMaybe (\(span, name) -> (,name) <$> toCurrentRange mapping span) tokens @@ -77,6 +84,7 @@ semanticTokensFull state _ param = do items <- runActionE "SemanticTokens.semanticTokensFull" state $ computeSemanticTokens state nfp return $ InL items + -- | Defines the 'getSemanticTokensRule' function, compute semantic tokens for a Haskell source file. -- -- This Rule collects information from various sources, including: @@ -87,13 +95,12 @@ semanticTokensFull state _ param = do -- Visible names from 'tmrRenamed' -- -- It then combines this information to compute the semantic tokens for the file. -getSemanticTokensRule :: Recorder (WithPriority Log) -> Rules () +getSemanticTokensRule :: Recorder (WithPriority SemanticLog) -> Rules () getSemanticTokensRule recorder = - define (cmapWithPrio LogShake recorder) $ \GetSemanticTokens nfp -> do - -- (hscEnv -> hsc) <- use_ GhcSessionDeps nfp - (HAR {..}) <- use_ GetHieAst nfp - (DKMap{getKindMap}) <- use_ GetDocMap nfp - Just (_, ast) <- return $ listToMaybe $ Map.toList $ getAsts hieAst + define (cmapWithPrio LogShake recorder) $ \GetSemanticTokens nfp -> handleError recorder $ do + (HAR {..}) <- lift $ use_ GetHieAst nfp + (DKMap{getKindMap}, _) <- lift $ useWithStale_ GetDocMap nfp + (_, ast) <- handleMaybe LogNoAST $ listToMaybe $ Map.toList $ getAsts hieAst -- get current location from the old ones let spanNamesMap = hieAstSpanNames ast let names = nameSetElemsStable $ unionNameSets $ Map.elems spanNamesMap @@ -104,7 +111,7 @@ getSemanticTokensRule recorder = -- let importedNameSemanticMap = computeImportedNameSemanticMap $ nameSetElemsStable nameSet let sMap = plusNameEnv_C (<>) importedNameSemanticMap localSemanticMap let rangeTokenType = extractSemanticTokensFromNames sMap spanNamesMap - return ([], Just $ RangeHsSemanticTokenTypes rangeTokenType) + return $ RangeHsSemanticTokenTypes rangeTokenType where -- ignore one already in discovered in local getTypeExclude :: NameEnv a @@ -121,3 +128,14 @@ getSemanticTokensRule recorder = -- | Persistent rule to ensure that semantic tokens doesn't block on startup persistentGetSemanticTokensRule :: Rules () persistentGetSemanticTokensRule = addPersistentRule GetSemanticTokens $ \_ -> pure $ Just (RangeHsSemanticTokenTypes mempty, idDelta, Nothing) + +-- taken from /haskell-language-server/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs +-- | Handle error in 'Action'. Returns an 'IdeResult' with no value and no diagnostics on error. (but writes log) +handleError :: Recorder (WithPriority msg) -> ExceptT msg Action a -> Action (IdeResult a) +handleError recorder action' = do + valueEither <- runExceptT action' + case valueEither of + Left msg -> do + logWith recorder Warning msg + pure $ toIdeResult (Left []) + Right value -> pure $ toIdeResult (Right value) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 4ca49f6bb9..5a12d0062c 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -92,7 +92,7 @@ isFunType a = case a of ForAllTy _ t -> isFunType t -- Development.IDE.GHC.Compat.Core.FunTy(pattern synonym), FunTyFlag which is used to distinguish -- (->, =>, etc..) - FunTy flg _ rhs-> if isVisibleFunArg flg then True else isFunType rhs + FunTy flg _ rhs-> isVisibleFunArg flg || isFunType rhs _x -> isFunTy a hieKindFunMasksKind :: HieKind a -> HieFunMaskKind a diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 4854790dfa..07ecc82c73 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -1,14 +1,16 @@ -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE StrictData #-} -{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE StrictData #-} +{-# LANGUAGE TypeFamilies #-} module Ide.Plugin.SemanticTokens.Types where import Control.DeepSeq (NFData (rnf), rwhnf) import qualified Data.Array as A import Data.Generics (Typeable) -import Development.IDE (RuleResult) +import Development.IDE (Pretty (pretty), RuleResult) +import qualified Development.IDE.Core.Shake as Shake import Development.IDE.GHC.Compat hiding (loc) import Development.IDE.Graph.Classes (Hashable) import GHC.Generics (Generic) @@ -78,3 +80,13 @@ type instance RuleResult GetSemanticTokens = RangeHsSemanticTokenTypes data HieFunMaskKind kind where HieFreshFun :: HieFunMaskKind Type HieFromDiskFun :: A.Array TypeIndex Bool -> HieFunMaskKind TypeIndex + +data SemanticLog = LogShake Shake.Log + | LogNoAST + deriving Show + +instance Pretty SemanticLog where + pretty theLog = case theLog of + LogShake shakeLog -> pretty shakeLog + LogNoAST -> "no HieAst exist for file" + diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index f0e241c0ca..002f93e2ab 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -61,7 +61,7 @@ testDataDir = "test" "testdata" mkFs :: [FS.FileTree] -> FS.VirtualFileTree mkFs = FS.mkVirtualFileTree testDataDir -semanticTokensPlugin :: Test.Hls.PluginTestDescriptor Log +semanticTokensPlugin :: Test.Hls.PluginTestDescriptor SemanticLog semanticTokensPlugin = Test.Hls.mkPluginTestDescriptor Ide.Plugin.SemanticTokens.descriptor "SemanticTokens" mkSemanticTokensParams :: TextDocumentIdentifier -> SemanticTokensParams @@ -160,7 +160,8 @@ semanticTokensTests = liftIO $ 1 @?= 1, goldenWithSemanticTokens "mixed constancy test result generated from one ghc version" "T1", goldenWithSemanticTokens "pattern bind" "TPatternSyn", - goldenWithSemanticTokens "type family" "TTypefamily" + goldenWithSemanticTokens "type family" "TTypefamily", + goldenWithSemanticTokens "TUnicodeSyntax" "TUnicodeSyntax" ] semanticTokensDataTypeTests = diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TUnicodeSyntax.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/TUnicodeSyntax.expected new file mode 100644 index 0000000000..0b94b7c045 --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TUnicodeSyntax.expected @@ -0,0 +1 @@ +3:1-4 TVariable "a\66560b" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/TUnicodeSyntax.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/TUnicodeSyntax.hs new file mode 100644 index 0000000000..1b8c7c1baa --- /dev/null +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/TUnicodeSyntax.hs @@ -0,0 +1,5 @@ +module TUnicodeSyntax where + +a𐐀b = "a𐐀b" + + From 31b31a261269cad8a2388da13ab3b783f1a7ce7d Mon Sep 17 00:00:00 2001 From: Patrick Date: Sun, 31 Dec 2023 16:49:27 +0800 Subject: [PATCH 61/73] fix: handle unicode in semantic tokens --- .../hls-semantic-tokens-plugin.cabal | 1 + .../src/Ide/Plugin/SemanticTokens/Internal.hs | 9 ++--- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 33 ++++++++++++------- .../src/Ide/Plugin/SemanticTokens/Query.hs | 22 ++++++++----- .../src/Ide/Plugin/SemanticTokens/Types.hs | 2 ++ .../src/Ide/Plugin/SemanticTokens/Utils.hs | 18 ++++++++++ .../hls-semantic-tokens-plugin/test/Main.hs | 22 ++++++++----- 7 files changed, 74 insertions(+), 33 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal index 4bec95ff42..e0854733dc 100644 --- a/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal +++ b/plugins/hls-semantic-tokens-plugin/hls-semantic-tokens-plugin.cabal @@ -76,6 +76,7 @@ test-suite tests , lens , lsp , ghc + , text-rope , lsp-test , text , data-default diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index bb8d4971ab..8838f2dbec 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -25,7 +25,6 @@ import Control.Monad.Except (ExceptT, liftEither, import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Trans (lift) import Control.Monad.Trans.Except (runExceptT) -import Data.Either (fromRight) import qualified Data.Map as Map import Data.Maybe (listToMaybe, mapMaybe) import qualified Data.Text as T @@ -34,7 +33,6 @@ import Development.IDE (Action, GetHieAst (GetHieAst), HieAstResult (HAR, hieAst, hieModule, refMap), IdeResult, IdeState, - Pretty (pretty), Priority (..), Recorder, Rules, WithPriority, cmapWithPrio, define, @@ -46,12 +44,14 @@ import Development.IDE.Core.PositionMapping (idDelta, toCurrentRange) import Development.IDE.Core.Rules (toIdeResult) import Development.IDE.Core.RuleTypes (DocAndKindMap (..)) import Development.IDE.Core.Shake (addPersistentRule, + getVirtualFile, useWithStale_) import Development.IDE.GHC.Compat hiding (Warning) import Ide.Logger (logWith) import Ide.Plugin.Error (PluginError (PluginInternalError), getNormalizedFilePathE, - handleMaybe) + handleMaybe, + handleMaybeM) import Ide.Plugin.SemanticTokens.Mappings import Ide.Plugin.SemanticTokens.Query import Ide.Plugin.SemanticTokens.Types @@ -101,8 +101,9 @@ getSemanticTokensRule recorder = (HAR {..}) <- lift $ use_ GetHieAst nfp (DKMap{getKindMap}, _) <- lift $ useWithStale_ GetDocMap nfp (_, ast) <- handleMaybe LogNoAST $ listToMaybe $ Map.toList $ getAsts hieAst + virtualFile <- handleMaybeM LogNoVF $ getVirtualFile nfp -- get current location from the old ones - let spanNamesMap = hieAstSpanNames ast + let spanNamesMap = hieAstSpanNames virtualFile ast let names = nameSetElemsStable $ unionNameSets $ Map.elems spanNamesMap let localSemanticMap = mkLocalNameSemanticFromAst names (hieKindFunMasksKind hieKind) refMap -- get imported name semantic map diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 5a12d0062c..c274821780 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -15,18 +15,20 @@ module Ide.Plugin.SemanticTokens.Mappings where import qualified Data.Array as A import Data.List.Extra (chunksOf, (!?)) import qualified Data.Map as Map +import Data.Maybe (mapMaybe) import qualified Data.Set as Set -import Data.Text (Text) +import Data.Text (Text, unpack) import Development.IDE (HieKind (HieFresh, HieFromDisk)) import Development.IDE.GHC.Compat -import Ide.Plugin.SemanticTokens.Types -import Ide.Plugin.SemanticTokens.Utils (recoverFunMaskArray) +import Ide.Plugin.SemanticTokens.Types hiding (tokens) +import Ide.Plugin.SemanticTokens.Utils (mkRange, recoverFunMaskArray) import Language.LSP.Protocol.Types (LspEnum (knownValues), SemanticTokenAbsolute (SemanticTokenAbsolute), SemanticTokenRelative (SemanticTokenRelative), SemanticTokenTypes (..), SemanticTokens (SemanticTokens), UInt, absolutizeTokens) +import Language.LSP.VFS hiding (line) -- * 1. Mapping semantic token type to and from the LSP default token type. @@ -140,16 +142,23 @@ type ActualToken = (UInt, UInt, UInt, HsSemanticTokenType, UInt) -- for debug and test. -- this function is used to recover the original tokens(with token in haskell token type zoon) -- from the lsp semantic tokens(with token in lsp token type zoon) -recoverSemanticTokens :: String -> SemanticTokens -> Either Text [SemanticTokenOriginal] -recoverSemanticTokens sourceCode (SemanticTokens _ xs) = fmap (tokenOrigin sourceCode) <$> dataActualToken xs +recoverSemanticTokens :: VirtualFile -> SemanticTokens -> Either Text [SemanticTokenOriginal] +recoverSemanticTokens vsf (SemanticTokens _ xs) = do + tokens <- dataActualToken xs + return $ mapMaybe (tokenOrigin sourceCode) tokens where - tokenOrigin :: [Char] -> ActualToken -> SemanticTokenOriginal - tokenOrigin sourceCode' (line, startChar, len, tokenType, _) = - -- convert back to count from 1 - SemanticTokenOriginal tokenType (Loc (line + 1) (startChar + 1) len) name - where - tLine = lines sourceCode' !? fromIntegral line - name = maybe "no source" (take (fromIntegral len) . drop (fromIntegral startChar)) tLine + sourceCode = unpack $ virtualFileText vsf + tokenOrigin :: [Char] -> ActualToken -> Maybe SemanticTokenOriginal + tokenOrigin sourceCode' (line, startChar, len, tokenType, _) = do + -- convert back to count from 1 + let range = mkRange line startChar len + CodePointRange (CodePointPosition x y) (CodePointPosition _ y1) <- rangeToCodePointRange vsf range + let line' = x + let startChar' = y + let len' = y1 - y + let tLine = lines sourceCode' !? fromIntegral line' + let name = maybe "no source" (take (fromIntegral len') . drop (fromIntegral startChar')) tLine + return $ SemanticTokenOriginal tokenType (Loc (line' + 1) (startChar' + 1) len') name dataActualToken :: [UInt] -> Either Text [ActualToken] dataActualToken dt = diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index f1a9077cbe..151238b660 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -10,23 +10,25 @@ module Ide.Plugin.SemanticTokens.Query where import Data.Either (rights) import Data.Foldable (fold) -import Data.Generics (everything) import qualified Data.List as List import qualified Data.Map as M import qualified Data.Map as Map -import Data.Maybe (listToMaybe, mapMaybe) +import Data.Maybe (fromMaybe, listToMaybe, + mapMaybe) import qualified Data.Set as S import qualified Data.Set as Set import Data.Text (Text) -import Development.IDE (HieKind, - realSrcSpanToRange) +import Development.IDE (realSrcSpanToRange) import Development.IDE.GHC.Compat -import Generics.SYB (mkQ) import Ide.Plugin.SemanticTokens.Mappings import Ide.Plugin.SemanticTokens.Types (HieFunMaskKind, HsSemanticTokenType, NameSemanticMap) +import Ide.Plugin.SemanticTokens.Utils (realSrcSpanToCodePointRange) import Language.LSP.Protocol.Types +import Language.LSP.VFS (CodePointRange, + VirtualFile, + codePointRangeToRange) import Prelude hiding (span) --------------------------------------------------------- @@ -66,13 +68,15 @@ nameNameSemanticFromHie hieKind rm ns = do -- | get only visible names from HieAST -- we care only the leaf node of the AST -- and filter out the derived and evidence names -hieAstSpanNames :: HieAST a -> M.Map Range NameSet -hieAstSpanNames ast = +hieAstSpanNames :: VirtualFile -> HieAST a -> M.Map Range NameSet +hieAstSpanNames vf ast = if null (nodeChildren ast) then getIds ast - else M.unionsWith unionNameSet $ map hieAstSpanNames (nodeChildren ast) + else M.unionsWith unionNameSet $ map (hieAstSpanNames vf) (nodeChildren ast) where - getIds ast' = M.singleton (realSrcSpanToRange $ nodeSpan ast') (getNodeIds' ast') + getIds ast' = fromMaybe mempty $ do + range <- codePointRangeToRange vf $ realSrcSpanToCodePointRange $ nodeSpan ast' + return $ M.singleton range (getNodeIds' ast') getNodeIds' = Map.foldl' combineNodeIds mempty . Map.filterWithKey (\k _ -> k == SourceInfo) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 07ecc82c73..c1300d1ae6 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -83,10 +83,12 @@ data HieFunMaskKind kind where data SemanticLog = LogShake Shake.Log | LogNoAST + | LogNoVF deriving Show instance Pretty SemanticLog where pretty theLog = case theLog of LogShake shakeLog -> pretty shakeLog LogNoAST -> "no HieAst exist for file" + LogNoVF -> "no VirtualSourceFile exist for file" diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs index 556720d79e..d7de3c0dcf 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs @@ -9,8 +9,12 @@ import qualified Data.Array as A import Data.ByteString (ByteString) import Data.ByteString.Char8 (unpack) import qualified Data.Map as Map +import Development.IDE (Position (..), Range (..)) import Development.IDE.GHC.Compat +import qualified Development.IDE.GHC.Compat as Compat import Ide.Plugin.SemanticTokens.Types +import Language.LSP.VFS (CodePointPosition (..), + CodePointRange (..)) import Prelude hiding (span) deriving instance Show DeclType @@ -119,4 +123,18 @@ recoverFunMaskArray flattened = unflattened go (HTyConApp _ _) = False +realSrcSpanToCodePointRange :: RealSrcSpan -> CodePointRange +realSrcSpanToCodePointRange real = + CodePointRange (realSrcLocToCodePointPosition $ Compat.realSrcSpanStart real) + (realSrcLocToCodePointPosition $ Compat.realSrcSpanEnd real) + +realSrcLocToCodePointPosition :: RealSrcLoc -> CodePointPosition +realSrcLocToCodePointPosition real = + CodePointPosition (fromIntegral $ srcLocLine real - 1) (fromIntegral $ srcLocCol real - 1) + +-- rangeToCodePointRange +-- mkRange :: Int -> Int -> Int -> Range +mkRange :: (Integral a1, Integral a2) => a1 -> a2 -> a2 -> Range +mkRange startLine startCol len = + Range (Position (fromIntegral startLine) (fromIntegral startCol)) (Position (fromIntegral startLine) (fromIntegral $ startCol + len)) diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 002f93e2ab..234cd88fb8 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -23,8 +23,11 @@ import qualified Data.Set as Set import Data.String (fromString) import Data.Text hiding (length, map, unlines) -import Development.IDE (getFileContents, runAction) +import qualified Data.Text.Utf16.Rope as Rope +import Development.IDE (getFileContents, runAction, + toNormalizedUri) import Development.IDE.Core.Rules (Log) +import Development.IDE.Core.Shake (getVirtualFile) import Development.IDE.Plugin.Test (WaitForIdeRuleResult (..)) import Development.IDE.Test (waitForBuildQueue) import Ide.Plugin.Error (getNormalizedFilePathE) @@ -36,12 +39,14 @@ import qualified Language.LSP.Protocol.Lens as L import Language.LSP.Protocol.Types (SemanticTokens (..), SemanticTokensParams (..), _L, type (|?) (..)) -import Language.LSP.Test (openDoc) +import qualified Language.LSP.Server as Lsp +import Language.LSP.Test (Session (..), openDoc) import qualified Language.LSP.Test as Test +import Language.LSP.VFS (VirtualFile (..)) import System.Environment.Blank import System.FilePath import Test.Hls (PluginTestDescriptor, - Session, TestName, + Session (..), TestName, TestTree, TextDocumentIdentifier, defaultTestRunner, @@ -90,11 +95,12 @@ goldenWithSemanticTokens title path = docSemanticTokensString :: TextDocumentIdentifier -> Session String docSemanticTokensString doc = do res <- Test.getSemanticTokens doc - content <- unpack <$> documentContents doc + textContent <- documentContents doc + let vfs = VirtualFile 0 0 (Rope.fromText textContent) let expect = [] case res ^? _L of Just tokens -> do - either (error . show) (return . unlines . map show) $ recoverSemanticTokens content tokens + either (error . show) (return . unlines . map show) $ recoverSemanticTokens vfs tokens _noTokens -> error "No tokens found" semanticTokensImportedTests :: TestTree @@ -130,8 +136,6 @@ semanticTokensTests = [ testCase "module import test" $ do let filePath1 = "./test/testdata/TModuleA.hs" let filePath2 = "./test/testdata/TModuleB.hs" - content1 <- liftIO $ Prelude.readFile filePath1 - content2 <- liftIO $ Prelude.readFile filePath2 let file1 = "TModuleA.hs" let file2 = "TModuleB.hs" @@ -149,12 +153,14 @@ semanticTokensTests = Left y -> error "TypeCheck2 failed" res2 <- Test.getSemanticTokens doc2 + textContent2 <- documentContents doc2 + let vfs = VirtualFile 0 0 (Rope.fromText textContent2) case res2 ^? _L of Just tokens -> do either (error . show) (\xs -> liftIO $ xs @?= expect) - $ recoverSemanticTokens content2 tokens + $ recoverSemanticTokens vfs tokens return () _ -> error "No tokens found" liftIO $ 1 @?= 1, From 83c56e40d15993e4db1e3f94c00bb7beeb5b0bb3 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sun, 31 Dec 2023 16:52:48 +0800 Subject: [PATCH 62/73] update KindMap to TyThingMap --- ghcide/src/Development/IDE/Core/RuleTypes.hs | 8 ++++---- ghcide/src/Development/IDE/Plugin/Completions.hs | 4 ++-- ghcide/src/Development/IDE/Spans/AtPoint.hs | 2 +- ghcide/src/Development/IDE/Spans/Common.hs | 4 ++-- ghcide/src/Development/IDE/Spans/Documentation.hs | 2 +- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 10 +++++----- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ghcide/src/Development/IDE/Core/RuleTypes.hs b/ghcide/src/Development/IDE/Core/RuleTypes.hs index 30251ee8d3..995bbc023e 100644 --- a/ghcide/src/Development/IDE/Core/RuleTypes.hs +++ b/ghcide/src/Development/IDE/Core/RuleTypes.hs @@ -238,14 +238,14 @@ type instance RuleResult GetHieAst = HieAstResult -- | A IntervalMap telling us what is in scope at each point type instance RuleResult GetBindings = Bindings -data DocAndKindMap = DKMap {getDocMap :: !DocMap, getKindMap :: !KindMap} -instance NFData DocAndKindMap where +data DocAndTyThingMap = DKMap {getDocMap :: !DocMap, getTyThingMap :: !TyThingMap} +instance NFData DocAndTyThingMap where rnf (DKMap a b) = rwhnf a `seq` rwhnf b -instance Show DocAndKindMap where +instance Show DocAndTyThingMap where show = const "docmap" -type instance RuleResult GetDocMap = DocAndKindMap +type instance RuleResult GetDocMap = DocAndTyThingMap -- | A GHC session that we reuse. type instance RuleResult GhcSession = HscEnvEq diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 2b3bcd9308..18d6bfa982 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -144,8 +144,8 @@ resolveCompletion ide _pid comp@CompletionItem{_detail,_documentation,_data_} ur #endif mdkm <- liftIO $ runIdeAction "CompletionResolve.GetDocMap" (shakeExtras ide) $ useWithStaleFast GetDocMap file let (dm,km) = case mdkm of - Just (DKMap docMap kindMap, _) -> (docMap,kindMap) - Nothing -> (mempty, mempty) + Just (DKMap docMap tyThingMap, _) -> (docMap,tyThingMap) + Nothing -> (mempty, mempty) doc <- case lookupNameEnv dm name of Just doc -> pure $ spanDocToMarkdown doc Nothing -> liftIO $ spanDocToMarkdown <$> getDocumentationTryGhc (hscEnv sess) name diff --git a/ghcide/src/Development/IDE/Spans/AtPoint.hs b/ghcide/src/Development/IDE/Spans/AtPoint.hs index 00f62e82e4..446e03271e 100644 --- a/ghcide/src/Development/IDE/Spans/AtPoint.hs +++ b/ghcide/src/Development/IDE/Spans/AtPoint.hs @@ -209,7 +209,7 @@ gotoDefinition withHieDb getHieFile ideOpts imports srcSpans pos atPoint :: IdeOptions -> HieAstResult - -> DocAndKindMap + -> DocAndTyThingMap -> HscEnv -> Position -> IO (Maybe (Maybe Range, [T.Text])) diff --git a/ghcide/src/Development/IDE/Spans/Common.hs b/ghcide/src/Development/IDE/Spans/Common.hs index 2ec1e98e94..dbdacfcd5c 100644 --- a/ghcide/src/Development/IDE/Spans/Common.hs +++ b/ghcide/src/Development/IDE/Spans/Common.hs @@ -12,7 +12,7 @@ module Development.IDE.Spans.Common ( , spanDocToMarkdown , spanDocToMarkdownForTest , DocMap -, KindMap +, TyThingMap ) where import Control.DeepSeq @@ -31,7 +31,7 @@ import qualified Documentation.Haddock.Parser as H import qualified Documentation.Haddock.Types as H type DocMap = NameEnv SpanDoc -type KindMap = NameEnv TyThing +type TyThingMap = NameEnv TyThing -- | Shows IEWrappedName, without any modifier, qualifier or unique identifier. #if MIN_VERSION_ghc(9,5,0) diff --git a/ghcide/src/Development/IDE/Spans/Documentation.hs b/ghcide/src/Development/IDE/Spans/Documentation.hs index e744791bcf..a5209005d5 100644 --- a/ghcide/src/Development/IDE/Spans/Documentation.hs +++ b/ghcide/src/Development/IDE/Spans/Documentation.hs @@ -39,7 +39,7 @@ mkDocMap :: HscEnv -> RefMap a -> TcGblEnv - -> IO DocAndKindMap + -> IO DocAndTyThingMap mkDocMap env rm this_mod = do #if MIN_VERSION_ghc(9,3,0) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 8838f2dbec..aa5cb5c314 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -42,7 +42,7 @@ import Development.IDE.Core.PluginUtils (runActionE, useWithStaleE) import Development.IDE.Core.PositionMapping (idDelta, toCurrentRange) import Development.IDE.Core.Rules (toIdeResult) -import Development.IDE.Core.RuleTypes (DocAndKindMap (..)) +import Development.IDE.Core.RuleTypes (DocAndTyThingMap (..)) import Development.IDE.Core.Shake (addPersistentRule, getVirtualFile, useWithStale_) @@ -99,7 +99,7 @@ getSemanticTokensRule :: Recorder (WithPriority SemanticLog) -> Rules () getSemanticTokensRule recorder = define (cmapWithPrio LogShake recorder) $ \GetSemanticTokens nfp -> handleError recorder $ do (HAR {..}) <- lift $ use_ GetHieAst nfp - (DKMap{getKindMap}, _) <- lift $ useWithStale_ GetDocMap nfp + (DKMap{getTyThingMap}, _) <- lift $ useWithStale_ GetDocMap nfp (_, ast) <- handleMaybe LogNoAST $ listToMaybe $ Map.toList $ getAsts hieAst virtualFile <- handleMaybeM LogNoVF $ getVirtualFile nfp -- get current location from the old ones @@ -108,7 +108,7 @@ getSemanticTokensRule recorder = let localSemanticMap = mkLocalNameSemanticFromAst names (hieKindFunMasksKind hieKind) refMap -- get imported name semantic map -- liftIO $ putStrLn $ unlines $ fmap showClearName $ nameSetElemsStable nameSet - let importedNameSemanticMap = foldr (getTypeExclude localSemanticMap getKindMap) emptyNameEnv names + let importedNameSemanticMap = foldr (getTypeExclude localSemanticMap getTyThingMap) emptyNameEnv names -- let importedNameSemanticMap = computeImportedNameSemanticMap $ nameSetElemsStable nameSet let sMap = plusNameEnv_C (<>) importedNameSemanticMap localSemanticMap let rangeTokenType = extractSemanticTokensFromNames sMap spanNamesMap @@ -120,10 +120,10 @@ getSemanticTokensRule recorder = -> Name -> NameEnv HsSemanticTokenType -> NameEnv HsSemanticTokenType - getTypeExclude localEnv kindMap n nameMap + getTypeExclude localEnv tyThingMap n nameMap | n `elemNameEnv` localEnv = nameMap | otherwise = - let tyThing = lookupNameEnv kindMap n in + let tyThing = lookupNameEnv tyThingMap n in maybe nameMap (extendNameEnv nameMap n) (tyThing >>= tyThingSemantic) -- | Persistent rule to ensure that semantic tokens doesn't block on startup From 71e65322db7fce456eae083c66b33eb19d90e489 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sun, 31 Dec 2023 18:17:20 +0800 Subject: [PATCH 63/73] cleanup --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 7 +- .../src/Ide/Plugin/SemanticTokens/Query.hs | 71 ++++++++++--------- .../src/Ide/Plugin/SemanticTokens/Types.hs | 5 +- 3 files changed, 43 insertions(+), 40 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index aa5cb5c314..8fe887f51a 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -11,7 +11,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UnicodeSyntax #-} @@ -25,6 +24,7 @@ import Control.Monad.Except (ExceptT, liftEither, import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Trans (lift) import Control.Monad.Trans.Except (runExceptT) +import qualified Data.Map as M import qualified Data.Map as Map import Data.Maybe (listToMaybe, mapMaybe) import qualified Data.Text as T @@ -74,9 +74,8 @@ computeSemanticTokens :: IdeState -> NormalizedFilePath -> ExceptT PluginError A computeSemanticTokens state nfp = do let dbg = logActionWith state Debug dbg $ "Computing semantic tokens for: " <> show nfp - (RangeHsSemanticTokenTypes {tokens}, mapping) <- useWithStaleE GetSemanticTokens nfp - let rangeTokens = mapMaybe (\(span, name) -> (,name) <$> toCurrentRange mapping span) tokens - withExceptT PluginInternalError $ liftEither $ semanticTokenAbsoluteSemanticTokens rangeTokens + (RangeHsSemanticTokenTypes {rangeSemanticMap}, mapping) <- useWithStaleE GetSemanticTokens nfp + withExceptT PluginInternalError $ liftEither $ rangeSemanticMapSemanticTokens mapping rangeSemanticMap semanticTokensFull :: PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull semanticTokensFull state _ param = do diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 151238b660..e8059f2505 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -5,31 +5,29 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} - module Ide.Plugin.SemanticTokens.Query where -import Data.Either (rights) -import Data.Foldable (fold) -import qualified Data.List as List -import qualified Data.Map as M -import qualified Data.Map as Map -import Data.Maybe (fromMaybe, listToMaybe, - mapMaybe) -import qualified Data.Set as S -import qualified Data.Set as Set -import Data.Text (Text) -import Development.IDE (realSrcSpanToRange) +import Data.Either (rights) +import Data.Foldable (fold) +import qualified Data.Map as M +import qualified Data.Map as Map +import Data.Maybe (fromMaybe, listToMaybe, + mapMaybe) +import qualified Data.Set as S +import qualified Data.Set as Set +import Data.Text (Text) +import Development.IDE.Core.PositionMapping (PositionMapping, + toCurrentRange) import Development.IDE.GHC.Compat import Ide.Plugin.SemanticTokens.Mappings -import Ide.Plugin.SemanticTokens.Types (HieFunMaskKind, - HsSemanticTokenType, - NameSemanticMap) -import Ide.Plugin.SemanticTokens.Utils (realSrcSpanToCodePointRange) +import Ide.Plugin.SemanticTokens.Types (HieFunMaskKind, + HsSemanticTokenType, + NameSemanticMap) +import Ide.Plugin.SemanticTokens.Utils (realSrcSpanToCodePointRange) import Language.LSP.Protocol.Types -import Language.LSP.VFS (CodePointRange, - VirtualFile, - codePointRangeToRange) -import Prelude hiding (span) +import Language.LSP.VFS (VirtualFile, + codePointRangeToRange) +import Prelude hiding (span) --------------------------------------------------------- @@ -74,9 +72,9 @@ hieAstSpanNames vf ast = then getIds ast else M.unionsWith unionNameSet $ map (hieAstSpanNames vf) (nodeChildren ast) where - getIds ast' = fromMaybe mempty $ do - range <- codePointRangeToRange vf $ realSrcSpanToCodePointRange $ nodeSpan ast' - return $ M.singleton range (getNodeIds' ast') + getIds ast' = fromMaybe mempty $ do + range <- codePointRangeToRange vf $ realSrcSpanToCodePointRange $ nodeSpan ast' + return $ M.singleton range (getNodeIds' ast') getNodeIds' = Map.foldl' combineNodeIds mempty . Map.filterWithKey (\k _ -> k == SourceInfo) @@ -85,16 +83,15 @@ hieAstSpanNames vf ast = combineNodeIds :: NameSet -> NodeInfo a -> NameSet ad `combineNodeIds` (NodeInfo _ _ bd) = ad `unionNameSet` xs where - xs = mkNameSet $ rights $ M.keys $ M.filterWithKey inclusion bd + xs = mkNameSet $ rights $ M.keys $ M.filterWithKey inclusion bd inclusion :: Identifier -> IdentifierDetails a -> Bool inclusion a b = not $ exclusion a b exclusion :: Identifier -> IdentifierDetails a -> Bool exclusion idt IdentifierDetails {identInfo = infos} = case idt of - Left _ -> True - Right name -> - isDerivedOccName (nameOccName name) - || any isEvidenceContext (S.toList infos) - + Left _ -> True + Right name -> + isDerivedOccName (nameOccName name) + || any isEvidenceContext (S.toList infos) ------------------------------------------------- @@ -102,8 +99,16 @@ hieAstSpanNames vf ast = ------------------------------------------------- -semanticTokenAbsoluteSemanticTokens :: [(Range, HsSemanticTokenType)] -> Either Text SemanticTokens -semanticTokenAbsoluteSemanticTokens = makeSemanticTokens defaultSemanticTokensLegend . List.sort . map (uncurry toAbsSemanticToken) +extractSemanticTokensFromNames :: NameSemanticMap -> M.Map Range NameSet -> M.Map Range HsSemanticTokenType +extractSemanticTokensFromNames nsm rnMap = Map.mapMaybe (foldMap (lookupNameEnv nsm) . nameSetElemsStable) rnMap + + +rangeSemanticMapSemanticTokens :: PositionMapping -> M.Map Range HsSemanticTokenType -> Either Text SemanticTokens +rangeSemanticMapSemanticTokens mapping = + makeSemanticTokens defaultSemanticTokensLegend + . mapMaybe (\(range, ty) -> flip toAbsSemanticToken ty <$> range) + . Map.toAscList + . M.mapKeys (\r -> toCurrentRange mapping r) where toAbsSemanticToken :: Range -> HsSemanticTokenType -> SemanticTokenAbsolute toAbsSemanticToken (Range (Position startLine startColumn) (Position _endLine endColumn)) tokenType = @@ -114,7 +119,3 @@ semanticTokenAbsoluteSemanticTokens = makeSemanticTokens defaultSemanticTokensLe (fromIntegral len) (toLspTokenType tokenType) [] - -extractSemanticTokensFromNames :: NameSemanticMap -> M.Map Range NameSet -> [(Range, HsSemanticTokenType)] -extractSemanticTokensFromNames nsm rnMap = xs - where xs = mapMaybe sequence (Map.toList $ Map.map (foldMap (lookupNameEnv nsm) . nameSetElemsStable) rnMap) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index c1300d1ae6..4f6309dcb9 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -1,5 +1,6 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GADTs #-} +{-# LANGUAGE InstanceSigs #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE StrictData #-} {-# LANGUAGE TypeFamilies #-} @@ -9,6 +10,7 @@ module Ide.Plugin.SemanticTokens.Types where import Control.DeepSeq (NFData (rnf), rwhnf) import qualified Data.Array as A import Data.Generics (Typeable) +import qualified Data.Map as M import Development.IDE (Pretty (pretty), RuleResult) import qualified Development.IDE.Core.Shake as Shake import Development.IDE.GHC.Compat hiding (loc) @@ -67,9 +69,10 @@ instance Hashable GetSemanticTokens instance NFData GetSemanticTokens -data RangeHsSemanticTokenTypes = RangeHsSemanticTokenTypes {tokens :: [(Range, HsSemanticTokenType)]} +data RangeHsSemanticTokenTypes = RangeHsSemanticTokenTypes {rangeSemanticMap :: M.Map Range HsSemanticTokenType} instance NFData RangeHsSemanticTokenTypes where + rnf :: RangeHsSemanticTokenTypes -> () rnf (RangeHsSemanticTokenTypes a) = rwhnf a instance Show RangeHsSemanticTokenTypes where From 25dea6a488bf61a9e26cd56f46aa20a136bb31bb Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 1 Jan 2024 00:06:34 +0800 Subject: [PATCH 64/73] add realSrcSpanToCodePointRange, realSrcLocToCodePointPosition to Development.IDE.GHC.Error --- ghcide/src/Development/IDE/GHC/Error.hs | 26 +++++++++++++++++++ .../src/Ide/Plugin/SemanticTokens/Internal.hs | 2 +- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 2 +- .../src/Ide/Plugin/SemanticTokens/Query.hs | 4 ++- .../src/Ide/Plugin/SemanticTokens/Utils.hs | 11 -------- 5 files changed, 31 insertions(+), 14 deletions(-) diff --git a/ghcide/src/Development/IDE/GHC/Error.hs b/ghcide/src/Development/IDE/GHC/Error.hs index 8b5c9edc29..c9fe0153d3 100644 --- a/ghcide/src/Development/IDE/GHC/Error.hs +++ b/ghcide/src/Development/IDE/GHC/Error.hs @@ -17,6 +17,8 @@ module Development.IDE.GHC.Error , realSrcSpanToRange , realSrcLocToPosition , realSrcSpanToLocation + , realSrcSpanToCodePointRange + , realSrcLocToCodePointPosition , srcSpanToFilename , rangeToSrcSpan , rangeToRealSrcSpan @@ -45,6 +47,8 @@ import Development.IDE.Types.Diagnostics as D import Development.IDE.Types.Location import GHC import Language.LSP.Protocol.Types (isSubrangeOf) +import Language.LSP.VFS (CodePointPosition (CodePointPosition), + CodePointRange (CodePointRange)) diagFromText :: T.Text -> D.DiagnosticSeverity -> SrcSpan -> T.Text -> FileDiagnostic @@ -86,6 +90,28 @@ realSrcLocToPosition :: RealSrcLoc -> Position realSrcLocToPosition real = Position (fromIntegral $ srcLocLine real - 1) (fromIntegral $ srcLocCol real - 1) +-- Note [Unicode support] +-- the current situation is: +-- LSP Positions use UTF-16 code units(Unicode may count as variable columns); +-- GHC use Unicode code points(Unicode count as one column). +-- To support unicode, ideally range should be in lsp standard, +-- and codePoint should be in ghc standard. +-- see https://github.com/haskell/lsp/pull/407 + +-- | Convert a GHC SrcSpan to CodePointRange +-- see Note [Unicode support] +realSrcSpanToCodePointRange :: RealSrcSpan -> CodePointRange +realSrcSpanToCodePointRange real = + CodePointRange + (realSrcLocToCodePointPosition $ Compat.realSrcSpanStart real) + (realSrcLocToCodePointPosition $ Compat.realSrcSpanEnd real) + +-- | Convert a GHC RealSrcLoc to CodePointPosition +-- see Note [Unicode support] +realSrcLocToCodePointPosition :: RealSrcLoc -> CodePointPosition +realSrcLocToCodePointPosition real = + CodePointPosition (fromIntegral $ srcLocLine real - 1) (fromIntegral $ srcLocCol real - 1) + -- | Extract a file name from a GHC SrcSpan (use message for unhelpful ones) -- FIXME This may not be an _absolute_ file name, needs fixing. srcSpanToFilename :: SrcSpan -> Maybe FilePath diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 8fe887f51a..d82c39aabb 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -94,7 +94,7 @@ semanticTokensFull state _ param = do -- Visible names from 'tmrRenamed' -- -- It then combines this information to compute the semantic tokens for the file. -getSemanticTokensRule :: Recorder (WithPriority SemanticLog) -> Rules () +getSemanticTokensRule :: Recorder (WithPriority SemanticLog) -> Rules () getSemanticTokensRule recorder = define (cmapWithPrio LogShake recorder) $ \GetSemanticTokens nfp -> handleError recorder $ do (HAR {..}) <- lift $ use_ GetHieAst nfp diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index c274821780..6d7360fd78 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -20,7 +20,7 @@ import qualified Data.Set as Set import Data.Text (Text, unpack) import Development.IDE (HieKind (HieFresh, HieFromDisk)) import Development.IDE.GHC.Compat -import Ide.Plugin.SemanticTokens.Types hiding (tokens) +import Ide.Plugin.SemanticTokens.Types import Ide.Plugin.SemanticTokens.Utils (mkRange, recoverFunMaskArray) import Language.LSP.Protocol.Types (LspEnum (knownValues), SemanticTokenAbsolute (SemanticTokenAbsolute), diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index e8059f2505..18285b254d 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -19,11 +19,11 @@ import Data.Text (Text) import Development.IDE.Core.PositionMapping (PositionMapping, toCurrentRange) import Development.IDE.GHC.Compat +import Development.IDE.GHC.Error (realSrcSpanToCodePointRange) import Ide.Plugin.SemanticTokens.Mappings import Ide.Plugin.SemanticTokens.Types (HieFunMaskKind, HsSemanticTokenType, NameSemanticMap) -import Ide.Plugin.SemanticTokens.Utils (realSrcSpanToCodePointRange) import Language.LSP.Protocol.Types import Language.LSP.VFS (VirtualFile, codePointRangeToRange) @@ -50,6 +50,8 @@ nameNameSemanticFromHie hieKind rm ns = do nameSemanticFromRefMap rm' name' = do spanInfos <- -- traceShow ("getting spans:", nameString) $ Map.lookup (Right name') rm' + -- let combinedFunction x = (identType . snd) x <|> (identInfo . snd) x + -- let result = foldMap (typeSemantic hieKind) $ listToMaybe $ mapMaybe combinedFunction spanInfos let typeTokenType = foldMap (typeSemantic hieKind) $ listToMaybe $ mapMaybe (identType . snd) spanInfos contextInfoTokenType <- foldMap (contextInfosMaybeTokenType . identInfo . snd) spanInfos fold [typeTokenType, Just contextInfoTokenType] diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs index d7de3c0dcf..e4a6e2ac68 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs @@ -123,18 +123,7 @@ recoverFunMaskArray flattened = unflattened go (HTyConApp _ _) = False -realSrcSpanToCodePointRange :: RealSrcSpan -> CodePointRange -realSrcSpanToCodePointRange real = - CodePointRange (realSrcLocToCodePointPosition $ Compat.realSrcSpanStart real) - (realSrcLocToCodePointPosition $ Compat.realSrcSpanEnd real) - - -realSrcLocToCodePointPosition :: RealSrcLoc -> CodePointPosition -realSrcLocToCodePointPosition real = - CodePointPosition (fromIntegral $ srcLocLine real - 1) (fromIntegral $ srcLocCol real - 1) - -- rangeToCodePointRange --- mkRange :: Int -> Int -> Int -> Range mkRange :: (Integral a1, Integral a2) => a1 -> a2 -> a2 -> Range mkRange startLine startCol len = Range (Position (fromIntegral startLine) (fromIntegral startCol)) (Position (fromIntegral startLine) (fromIntegral $ startCol + len)) From 0b697e827a30130a7de75fca72f50050950ed3d8 Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 1 Jan 2024 00:48:35 +0800 Subject: [PATCH 65/73] add Note [Semantic information from Multiple Sources] --- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 6d7360fd78..afbc547dc5 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -35,9 +35,9 @@ import Language.LSP.VFS hiding (line) -- | map from haskell semantic token type to LSP default token type toLspTokenType :: HsSemanticTokenType -> SemanticTokenTypes toLspTokenType tk = case tk of - -- TVariable -> SemanticTokenTypes_Variable - -- left hand side of none pattern bind + -- Function type variable TFunction -> SemanticTokenTypes_Function + -- None function type variable TVariable -> SemanticTokenTypes_Variable TClass -> SemanticTokenTypes_Class TClassMethod -> SemanticTokenTypes_Method @@ -48,7 +48,7 @@ toLspTokenType tk = case tk of TTypeCon -> SemanticTokenTypes_Enum TDataCon -> SemanticTokenTypes_EnumMember TRecField -> SemanticTokenTypes_Property - -- pattern syn is like a limited version of macro of constructing a data type + -- pattern syn is like a limited version of macro of constructing a term TPatternSyn -> SemanticTokenTypes_Macro -- saturated type TTypeSyn -> SemanticTokenTypes_Type @@ -178,3 +178,21 @@ recoverSemanticTokens vsf (SemanticTokens _ xs) = do -- legends :: SemanticTokensLegend fromInt :: Int -> Maybe SemanticTokenTypes fromInt i = Set.toAscList knownValues !? i + + +-- Note [Semantic information from Multiple Sources] +-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-- We group Name into 2 categories since the information source is different: +-- 1. Locally defined Name +-- Information source is current module's HieAst, +-- Either from ContextInfo(all except differing function and none-function) +-- or from Hie Type(Differing Function and Non-function Variable) +-- 2. Imported Name +-- Information source is `TyThing` for the `Name`, looked up in `HscEnv`(with all imported things loaded). +-- `TyThing` is information rich, since it is used to represent the things that a name can refer to in ghc. +-- The reason why we need special handling for imported name is that +-- Up to 9.8 +-- 1. For Hie Type, IfaceTyCon in hie type does not contain enough information to distinguish class, type syn, type family etc.. +-- 2. Most imported name is only annotated as [Use] in the ContextInfo from hie. +-- 3. `namespace` in `Name` is limited, we can only classify `VarName, FldName, DataName, TvNamem, TcClsName`. +-- 4. WiredIn `Name` have `TyThing` attached, but not many are WiredIn names. From 1c2bf5b79879ee2e977fc4dd6d1accea830621d8 Mon Sep 17 00:00:00 2001 From: Patrick Date: Tue, 2 Jan 2024 20:58:20 +0800 Subject: [PATCH 66/73] move recoverFunMaskArray to Mappings.hs --- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 28 ++++++++++++++++++- .../src/Ide/Plugin/SemanticTokens/Utils.hs | 24 ---------------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index afbc547dc5..b32218ed9d 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -21,7 +21,7 @@ import Data.Text (Text, unpack) import Development.IDE (HieKind (HieFresh, HieFromDisk)) import Development.IDE.GHC.Compat import Ide.Plugin.SemanticTokens.Types -import Ide.Plugin.SemanticTokens.Utils (mkRange, recoverFunMaskArray) +import Ide.Plugin.SemanticTokens.Utils (mkRange) import Language.LSP.Protocol.Types (LspEnum (knownValues), SemanticTokenAbsolute (SemanticTokenAbsolute), SemanticTokenRelative (SemanticTokenRelative), @@ -62,6 +62,7 @@ lspTokenReverseMap = Map.fromList $ map (\x -> (toLspTokenType x, x)) $ enumFrom fromLspTokenType :: SemanticTokenTypes -> Maybe HsSemanticTokenType fromLspTokenType tk = Map.lookup tk lspTokenReverseMap + -- * 2. Mapping from GHC type and tyThing to semantic token type. @@ -102,6 +103,30 @@ hieKindFunMasksKind hieKind = case hieKind of HieFresh -> HieFreshFun HieFromDisk full_file -> HieFromDiskFun $ recoverFunMaskArray (hie_types full_file) +-- wz1000 offered +-- the idea from https://gitlab.haskell.org/ghc/haddock/-/blob/b0b0e0366457c9aefebcc94df74e5de4d00e17b7/haddock-api/src/Haddock/Backends/Hyperlinker/Utils.hs#L107 +recoverFunMaskArray + :: A.Array TypeIndex HieTypeFlat -- ^ flat types + -> A.Array TypeIndex Bool-- ^ full AST +recoverFunMaskArray flattened = unflattened + where + -- The recursion in 'unflattened' is crucial - it's what gives us sharing + -- between the IfaceType's produced + unflattened :: A.Array TypeIndex Bool + unflattened = fmap (\flatTy -> go (fmap (unflattened A.!) flatTy)) flattened + + -- Unfold an 'HieType' whose subterms have already been unfolded + go :: HieType Bool -> Bool + go (HTyVarTy _name) = False + go (HAppTy _f _x) = False + go (HLitTy _lit) = False + go (HForAllTy ((_n,_k),_af) b) = b + go (HFunTy _ _ _) = True + go (HQualTy _constraint b) = b + go (HCastTy b) = b + go HCoercionTy = False + go (HTyConApp _ _) = False + typeSemantic :: HieFunMaskKind hType -> hType -> Maybe HsSemanticTokenType typeSemantic kind t = case kind of HieFreshFun -> if isFunType t then Just TFunction else Nothing @@ -147,6 +172,7 @@ recoverSemanticTokens vsf (SemanticTokens _ xs) = do tokens <- dataActualToken xs return $ mapMaybe (tokenOrigin sourceCode) tokens where + -- xxxxx sourceCode = unpack $ virtualFileText vsf tokenOrigin :: [Char] -> ActualToken -> Maybe SemanticTokenOriginal tokenOrigin sourceCode' (line, startChar, len, tokenType, _) = do diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs index e4a6e2ac68..65de02e578 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs @@ -99,30 +99,6 @@ showSpan :: RealSrcSpan -> String showSpan x = show (srcSpanStartLine x) <> ":" <> show (srcSpanStartCol x) <> "-" <> show (srcSpanEndCol x) --- idea from https://gitlab.haskell.org/ghc/haddock/-/blob/b0b0e0366457c9aefebcc94df74e5de4d00e17b7/haddock-api/src/Haddock/Backends/Hyperlinker/Utils.hs#L107 -recoverFunMaskArray - :: A.Array TypeIndex HieTypeFlat -- ^ flat types - -> A.Array TypeIndex Bool-- ^ full AST -recoverFunMaskArray flattened = unflattened - where - -- The recursion in 'unflattened' is crucial - it's what gives us sharing - -- between the IfaceType's produced - unflattened :: A.Array TypeIndex Bool - unflattened = fmap (\flatTy -> go (fmap (unflattened A.!) flatTy)) flattened - - -- Unfold an 'HieType' whose subterms have already been unfolded - go :: HieType Bool -> Bool - go (HTyVarTy _name) = False - go (HAppTy _f _x) = False - go (HLitTy _lit) = False - go (HForAllTy ((_n,_k),_af) b) = b - go (HFunTy _ _ _) = True - go (HQualTy _constraint b) = b - go (HCastTy b) = b - go HCoercionTy = False - go (HTyConApp _ _) = False - - -- rangeToCodePointRange mkRange :: (Integral a1, Integral a2) => a1 -> a2 -> a2 -> Range mkRange startLine startCol len = From 343dd69942c5ee1222a2129d97c29496c956d45a Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 5 Jan 2024 15:47:03 +0800 Subject: [PATCH 67/73] fix test, data.Set might not appear --- plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected | 2 -- plugins/hls-semantic-tokens-plugin/test/testdata/T1.hs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected b/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected index d23585c20e..8e00ed86de 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/T1.expected @@ -1,5 +1,3 @@ -6:18-21 TTypeCon "Set" -6:23-29 TFunction "insert" 9:6-9 TTypeCon "Foo" 9:12-15 TDataCon "Foo" 9:18-21 TRecField "foo" diff --git a/plugins/hls-semantic-tokens-plugin/test/testdata/T1.hs b/plugins/hls-semantic-tokens-plugin/test/testdata/T1.hs index 8e044b3a39..07b0476c1e 100644 --- a/plugins/hls-semantic-tokens-plugin/test/testdata/T1.hs +++ b/plugins/hls-semantic-tokens-plugin/test/testdata/T1.hs @@ -3,7 +3,7 @@ module Main where -import Data.Set (Set, insert) +-- import Data.Set (Set, insert) data Foo = Foo { foo :: Int } From fc440f5ecbe4df5b583ff54da465a9585cdccf07 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 5 Jan 2024 16:12:19 +0800 Subject: [PATCH 68/73] fix: handle semantic tokens with more than one ast --- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 10 +++++----- .../src/Ide/Plugin/SemanticTokens/Types.hs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index d82c39aabb..3a643690c0 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -23,7 +23,7 @@ import Control.Monad.Except (ExceptT, liftEither, withExceptT) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Trans (lift) -import Control.Monad.Trans.Except (runExceptT) +import Control.Monad.Trans.Except (runExceptT, throwE) import qualified Data.Map as M import qualified Data.Map as Map import Data.Maybe (listToMaybe, mapMaybe) @@ -36,6 +36,7 @@ import Development.IDE (Action, Priority (..), Recorder, Rules, WithPriority, cmapWithPrio, define, + fromNormalizedFilePath, hieKind, ideLogger, logPriority, use_) import Development.IDE.Core.PluginUtils (runActionE, @@ -47,6 +48,7 @@ import Development.IDE.Core.Shake (addPersistentRule, getVirtualFile, useWithStale_) import Development.IDE.GHC.Compat hiding (Warning) +import Development.IDE.GHC.Compat.Util (mkFastString) import Ide.Logger (logWith) import Ide.Plugin.Error (PluginError (PluginInternalError), getNormalizedFilePathE, @@ -71,9 +73,7 @@ logActionWith st prior = liftIO . logPriority (ideLogger st) prior . T.pack ----------------------- computeSemanticTokens :: IdeState -> NormalizedFilePath -> ExceptT PluginError Action SemanticTokens -computeSemanticTokens state nfp = do - let dbg = logActionWith state Debug - dbg $ "Computing semantic tokens for: " <> show nfp +computeSemanticTokens _ nfp = do (RangeHsSemanticTokenTypes {rangeSemanticMap}, mapping) <- useWithStaleE GetSemanticTokens nfp withExceptT PluginInternalError $ liftEither $ rangeSemanticMapSemanticTokens mapping rangeSemanticMap @@ -99,7 +99,7 @@ getSemanticTokensRule recorder = define (cmapWithPrio LogShake recorder) $ \GetSemanticTokens nfp -> handleError recorder $ do (HAR {..}) <- lift $ use_ GetHieAst nfp (DKMap{getTyThingMap}, _) <- lift $ useWithStale_ GetDocMap nfp - (_, ast) <- handleMaybe LogNoAST $ listToMaybe $ Map.toList $ getAsts hieAst + ast <- handleMaybe (LogNoAST $ show nfp) $ getAsts hieAst Map.!? (HiePath. mkFastString . fromNormalizedFilePath) nfp virtualFile <- handleMaybeM LogNoVF $ getVirtualFile nfp -- get current location from the old ones let spanNamesMap = hieAstSpanNames virtualFile ast diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index 4f6309dcb9..f6149dd0f2 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -85,13 +85,13 @@ data HieFunMaskKind kind where HieFromDiskFun :: A.Array TypeIndex Bool -> HieFunMaskKind TypeIndex data SemanticLog = LogShake Shake.Log - | LogNoAST + | LogNoAST FilePath | LogNoVF deriving Show instance Pretty SemanticLog where pretty theLog = case theLog of LogShake shakeLog -> pretty shakeLog - LogNoAST -> "no HieAst exist for file" + LogNoAST path -> "no HieAst exist for file" <> pretty path LogNoVF -> "no VirtualSourceFile exist for file" From 517d52b7ffe254c4d756c6e3bb57d39a1b1fd45c Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 5 Jan 2024 16:26:07 +0800 Subject: [PATCH 69/73] fix: instance PluginMethod Request Method_TextDocumentSemanticTokensFull --- hls-plugin-api/src/Ide/Types.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index 14c846afdd..51ae0426bd 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -517,6 +517,9 @@ instance PluginMethod Request Method_TextDocumentRangeFormatting where where pid = pluginId pluginDesc +instance PluginMethod Request Method_TextDocumentSemanticTokensFull where + handlesRequest = pluginEnabledWithFeature plcCallHierarchyOn + instance PluginMethod Request Method_TextDocumentPrepareCallHierarchy where handlesRequest = pluginEnabledWithFeature plcCallHierarchyOn From e897f81b2f480a0c528cf8b01357e0e5bff46985 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 5 Jan 2024 22:51:21 +0800 Subject: [PATCH 70/73] clean up --- .../src/Ide/Plugin/SemanticTokens.hs | 12 ++-- .../src/Ide/Plugin/SemanticTokens/Internal.hs | 49 +++++++------- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 65 +++++++++---------- .../src/Ide/Plugin/SemanticTokens/Query.hs | 13 +--- .../src/Ide/Plugin/SemanticTokens/Types.hs | 22 +++---- .../src/Ide/Plugin/SemanticTokens/Utils.hs | 4 -- .../hls-semantic-tokens-plugin/test/Main.hs | 4 +- 7 files changed, 74 insertions(+), 95 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index 34b4d480cb..2ca8453a2b 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} + module Ide.Plugin.SemanticTokens (descriptor) where import Development.IDE @@ -7,13 +8,12 @@ import Ide.Plugin.SemanticTokens.Types import Ide.Types import Language.LSP.Protocol.Message - - descriptor :: Recorder (WithPriority SemanticLog) -> PluginId -> PluginDescriptor IdeState -descriptor recorder plId = (defaultPluginDescriptor plId "Provides semantic tokens") +descriptor recorder plId = + (defaultPluginDescriptor plId "Provides semantic tokens") { Ide.Types.pluginHandlers = - mkPluginHandler SMethod_TextDocumentSemanticTokensFull Internal.semanticTokensFull - , Ide.Types.pluginRules = + mkPluginHandler SMethod_TextDocumentSemanticTokensFull Internal.semanticTokensFull, + Ide.Types.pluginRules = Internal.getSemanticTokensRule recorder - <> Internal.persistentGetSemanticTokensRule + <> Internal.persistentGetSemanticTokensRule } diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs index 3a643690c0..9e69a213c8 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Internal.hs @@ -1,7 +1,4 @@ ----------------------------------------------------------------------------- --- | --- This module provides the core functionality of the plugin. --- ----------------------------------------------------------------------------- {-# LANGUAGE DataKinds #-} {-# LANGUAGE DerivingStrategies #-} @@ -23,10 +20,8 @@ import Control.Monad.Except (ExceptT, liftEither, withExceptT) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Trans (lift) -import Control.Monad.Trans.Except (runExceptT, throwE) -import qualified Data.Map as M +import Control.Monad.Trans.Except (runExceptT) import qualified Data.Map as Map -import Data.Maybe (listToMaybe, mapMaybe) import qualified Data.Text as T import Development.IDE (Action, GetDocMap (GetDocMap), @@ -41,7 +36,7 @@ import Development.IDE (Action, logPriority, use_) import Development.IDE.Core.PluginUtils (runActionE, useWithStaleE) -import Development.IDE.Core.PositionMapping (idDelta, toCurrentRange) +import Development.IDE.Core.PositionMapping (idDelta) import Development.IDE.Core.Rules (toIdeResult) import Development.IDE.Core.RuleTypes (DocAndTyThingMap (..)) import Development.IDE.Core.Shake (addPersistentRule, @@ -73,7 +68,8 @@ logActionWith st prior = liftIO . logPriority (ideLogger st) prior . T.pack ----------------------- computeSemanticTokens :: IdeState -> NormalizedFilePath -> ExceptT PluginError Action SemanticTokens -computeSemanticTokens _ nfp = do +computeSemanticTokens st nfp = do + logActionWith st Debug $ "Computing semantic tokens:" <> show nfp (RangeHsSemanticTokenTypes {rangeSemanticMap}, mapping) <- useWithStaleE GetSemanticTokens nfp withExceptT PluginInternalError $ liftEither $ rangeSemanticMapSemanticTokens mapping rangeSemanticMap @@ -83,12 +79,11 @@ semanticTokensFull state _ param = do items <- runActionE "SemanticTokens.semanticTokensFull" state $ computeSemanticTokens state nfp return $ InL items - -- | Defines the 'getSemanticTokensRule' function, compute semantic tokens for a Haskell source file. -- -- This Rule collects information from various sources, including: -- --- Imported name token type from Rule 'GetSemanticTokens' +-- Imported name token type from Rule 'GetDocMap' -- Local names token type from 'hieAst' -- Name locations from 'hieAst' -- Visible names from 'tmrRenamed' @@ -98,44 +93,44 @@ getSemanticTokensRule :: Recorder (WithPriority SemanticLog) -> Rules () getSemanticTokensRule recorder = define (cmapWithPrio LogShake recorder) $ \GetSemanticTokens nfp -> handleError recorder $ do (HAR {..}) <- lift $ use_ GetHieAst nfp - (DKMap{getTyThingMap}, _) <- lift $ useWithStale_ GetDocMap nfp - ast <- handleMaybe (LogNoAST $ show nfp) $ getAsts hieAst Map.!? (HiePath. mkFastString . fromNormalizedFilePath) nfp + (DKMap {getTyThingMap}, _) <- lift $ useWithStale_ GetDocMap nfp + ast <- handleMaybe (LogNoAST $ show nfp) $ getAsts hieAst Map.!? (HiePath . mkFastString . fromNormalizedFilePath) nfp virtualFile <- handleMaybeM LogNoVF $ getVirtualFile nfp -- get current location from the old ones let spanNamesMap = hieAstSpanNames virtualFile ast let names = nameSetElemsStable $ unionNameSets $ Map.elems spanNamesMap let localSemanticMap = mkLocalNameSemanticFromAst names (hieKindFunMasksKind hieKind) refMap -- get imported name semantic map - -- liftIO $ putStrLn $ unlines $ fmap showClearName $ nameSetElemsStable nameSet let importedNameSemanticMap = foldr (getTypeExclude localSemanticMap getTyThingMap) emptyNameEnv names - -- let importedNameSemanticMap = computeImportedNameSemanticMap $ nameSetElemsStable nameSet let sMap = plusNameEnv_C (<>) importedNameSemanticMap localSemanticMap let rangeTokenType = extractSemanticTokensFromNames sMap spanNamesMap return $ RangeHsSemanticTokenTypes rangeTokenType where -- ignore one already in discovered in local - getTypeExclude :: NameEnv a - -> NameEnv TyThing - -> Name - -> NameEnv HsSemanticTokenType - -> NameEnv HsSemanticTokenType + getTypeExclude :: + NameEnv a -> + NameEnv TyThing -> + Name -> + NameEnv HsSemanticTokenType -> + NameEnv HsSemanticTokenType getTypeExclude localEnv tyThingMap n nameMap | n `elemNameEnv` localEnv = nameMap | otherwise = - let tyThing = lookupNameEnv tyThingMap n in - maybe nameMap (extendNameEnv nameMap n) (tyThing >>= tyThingSemantic) + let tyThing = lookupNameEnv tyThingMap n + in maybe nameMap (extendNameEnv nameMap n) (tyThing >>= tyThingSemantic) -- | Persistent rule to ensure that semantic tokens doesn't block on startup persistentGetSemanticTokensRule :: Rules () persistentGetSemanticTokensRule = addPersistentRule GetSemanticTokens $ \_ -> pure $ Just (RangeHsSemanticTokenTypes mempty, idDelta, Nothing) -- taken from /haskell-language-server/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs + -- | Handle error in 'Action'. Returns an 'IdeResult' with no value and no diagnostics on error. (but writes log) handleError :: Recorder (WithPriority msg) -> ExceptT msg Action a -> Action (IdeResult a) handleError recorder action' = do - valueEither <- runExceptT action' - case valueEither of - Left msg -> do - logWith recorder Warning msg - pure $ toIdeResult (Left []) - Right value -> pure $ toIdeResult (Right value) + valueEither <- runExceptT action' + case valueEither of + Left msg -> do + logWith recorder Warning msg + pure $ toIdeResult (Left []) + Right value -> pure $ toIdeResult (Right value) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index b32218ed9d..80ea885447 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -1,6 +1,5 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} -- | @@ -55,17 +54,14 @@ toLspTokenType tk = case tk of -- not sure if this is correct choice TTypeFamily -> SemanticTokenTypes_Interface - lspTokenReverseMap :: Map.Map SemanticTokenTypes HsSemanticTokenType lspTokenReverseMap = Map.fromList $ map (\x -> (toLspTokenType x, x)) $ enumFrom minBound fromLspTokenType :: SemanticTokenTypes -> Maybe HsSemanticTokenType fromLspTokenType tk = Map.lookup tk lspTokenReverseMap - -- * 2. Mapping from GHC type and tyThing to semantic token type. - -- | tyThingSemantic tyThingSemantic :: TyThing -> Maybe HsSemanticTokenType tyThingSemantic ty = case ty of @@ -74,7 +70,6 @@ tyThingSemantic ty = case ty of | isRecordSelector vid -> Just TRecField | isClassOpId vid -> Just TClassMethod | isFunVar vid -> Just TFunction - -- \| isDFunId vid -> TClassMethod | otherwise -> Just TVariable AConLike con -> case con of RealDataCon _ -> Just TDataCon @@ -93,9 +88,9 @@ tyThingSemantic ty = case ty of isFunType :: Type -> Bool isFunType a = case a of ForAllTy _ t -> isFunType t --- Development.IDE.GHC.Compat.Core.FunTy(pattern synonym), FunTyFlag which is used to distinguish --- (->, =>, etc..) - FunTy flg _ rhs-> isVisibleFunArg flg || isFunType rhs + -- Development.IDE.GHC.Compat.Core.FunTy(pattern synonym), FunTyFlag which is used to distinguish + -- (->, =>, etc..) + FunTy flg _ rhs -> isVisibleFunArg flg || isFunType rhs _x -> isFunTy a hieKindFunMasksKind :: HieKind a -> HieFunMaskKind a @@ -105,27 +100,29 @@ hieKindFunMasksKind hieKind = case hieKind of -- wz1000 offered -- the idea from https://gitlab.haskell.org/ghc/haddock/-/blob/b0b0e0366457c9aefebcc94df74e5de4d00e17b7/haddock-api/src/Haddock/Backends/Hyperlinker/Utils.hs#L107 -recoverFunMaskArray - :: A.Array TypeIndex HieTypeFlat -- ^ flat types - -> A.Array TypeIndex Bool-- ^ full AST +recoverFunMaskArray :: + -- | flat types + A.Array TypeIndex HieTypeFlat -> + -- | array of bool indicating whether the type is a function + A.Array TypeIndex Bool recoverFunMaskArray flattened = unflattened - where + where -- The recursion in 'unflattened' is crucial - it's what gives us sharing - -- between the IfaceType's produced + -- between the elements of the array. unflattened :: A.Array TypeIndex Bool unflattened = fmap (\flatTy -> go (fmap (unflattened A.!) flatTy)) flattened -- Unfold an 'HieType' whose subterms have already been unfolded go :: HieType Bool -> Bool - go (HTyVarTy _name) = False - go (HAppTy _f _x) = False - go (HLitTy _lit) = False - go (HForAllTy ((_n,_k),_af) b) = b - go (HFunTy _ _ _) = True - go (HQualTy _constraint b) = b - go (HCastTy b) = b - go HCoercionTy = False - go (HTyConApp _ _) = False + go (HTyVarTy _name) = False + go (HAppTy _f _x) = False + go (HLitTy _lit) = False + go (HForAllTy ((_n, _k), _af) b) = b + go (HFunTy _ _ _) = True + go (HQualTy _constraint b) = b + go (HCastTy b) = b + go HCoercionTy = False + go (HTyConApp _ _) = False typeSemantic :: HieFunMaskKind hType -> hType -> Maybe HsSemanticTokenType typeSemantic kind t = case kind of @@ -169,22 +166,21 @@ type ActualToken = (UInt, UInt, UInt, HsSemanticTokenType, UInt) -- from the lsp semantic tokens(with token in lsp token type zoon) recoverSemanticTokens :: VirtualFile -> SemanticTokens -> Either Text [SemanticTokenOriginal] recoverSemanticTokens vsf (SemanticTokens _ xs) = do - tokens <- dataActualToken xs - return $ mapMaybe (tokenOrigin sourceCode) tokens + tokens <- dataActualToken xs + return $ mapMaybe (tokenOrigin sourceCode) tokens where - -- xxxxx sourceCode = unpack $ virtualFileText vsf tokenOrigin :: [Char] -> ActualToken -> Maybe SemanticTokenOriginal tokenOrigin sourceCode' (line, startChar, len, tokenType, _) = do - -- convert back to count from 1 - let range = mkRange line startChar len - CodePointRange (CodePointPosition x y) (CodePointPosition _ y1) <- rangeToCodePointRange vsf range - let line' = x - let startChar' = y - let len' = y1 - y - let tLine = lines sourceCode' !? fromIntegral line' - let name = maybe "no source" (take (fromIntegral len') . drop (fromIntegral startChar')) tLine - return $ SemanticTokenOriginal tokenType (Loc (line' + 1) (startChar' + 1) len') name + -- convert back to count from 1 + let range = mkRange line startChar len + CodePointRange (CodePointPosition x y) (CodePointPosition _ y1) <- rangeToCodePointRange vsf range + let line' = x + let startChar' = y + let len' = y1 - y + let tLine = lines sourceCode' !? fromIntegral line' + let name = maybe "no source" (take (fromIntegral len') . drop (fromIntegral startChar')) tLine + return $ SemanticTokenOriginal tokenType (Loc (line' + 1) (startChar' + 1) len') name dataActualToken :: [UInt] -> Either Text [ActualToken] dataActualToken dt = @@ -205,7 +201,6 @@ recoverSemanticTokens vsf (SemanticTokens _ xs) = do fromInt :: Int -> Maybe SemanticTokenTypes fromInt i = Set.toAscList knownValues !? i - -- Note [Semantic information from Multiple Sources] -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- We group Name into 2 categories since the information source is different: diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 18285b254d..99884bb879 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -40,18 +40,12 @@ mkLocalNameSemanticFromAst names hieKind rm = mkNameEnv (mapMaybe (nameNameSeman nameNameSemanticFromHie :: forall a. HieFunMaskKind a -> RefMap a -> Name -> Maybe (Name, HsSemanticTokenType) nameNameSemanticFromHie hieKind rm ns = do - st <- -- traceShow ("to find Name", showName ns) $ - nameSemanticFromRefMap rm ns - return - -- \$ traceShow (showName ns, st) - (ns, st) + st <- nameSemanticFromRefMap rm ns + return (ns, st) where nameSemanticFromRefMap :: RefMap a -> Name -> Maybe HsSemanticTokenType nameSemanticFromRefMap rm' name' = do - spanInfos <- -- traceShow ("getting spans:", nameString) $ - Map.lookup (Right name') rm' - -- let combinedFunction x = (identType . snd) x <|> (identInfo . snd) x - -- let result = foldMap (typeSemantic hieKind) $ listToMaybe $ mapMaybe combinedFunction spanInfos + spanInfos <- Map.lookup (Right name') rm' let typeTokenType = foldMap (typeSemantic hieKind) $ listToMaybe $ mapMaybe (identType . snd) spanInfos contextInfoTokenType <- foldMap (contextInfosMaybeTokenType . identInfo . snd) spanInfos fold [typeTokenType, Just contextInfoTokenType] @@ -104,7 +98,6 @@ hieAstSpanNames vf ast = extractSemanticTokensFromNames :: NameSemanticMap -> M.Map Range NameSet -> M.Map Range HsSemanticTokenType extractSemanticTokensFromNames nsm rnMap = Map.mapMaybe (foldMap (lookupNameEnv nsm) . nameSetElemsStable) rnMap - rangeSemanticMapSemanticTokens :: PositionMapping -> M.Map Range HsSemanticTokenType -> Either Text SemanticTokens rangeSemanticMapSemanticTokens mapping = makeSemanticTokens defaultSemanticTokensLegend diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs index f6149dd0f2..a6fb63c0c0 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Types.hs @@ -81,17 +81,17 @@ instance Show RangeHsSemanticTokenTypes where type instance RuleResult GetSemanticTokens = RangeHsSemanticTokenTypes data HieFunMaskKind kind where - HieFreshFun :: HieFunMaskKind Type - HieFromDiskFun :: A.Array TypeIndex Bool -> HieFunMaskKind TypeIndex + HieFreshFun :: HieFunMaskKind Type + HieFromDiskFun :: A.Array TypeIndex Bool -> HieFunMaskKind TypeIndex -data SemanticLog = LogShake Shake.Log - | LogNoAST FilePath - | LogNoVF - deriving Show +data SemanticLog + = LogShake Shake.Log + | LogNoAST FilePath + | LogNoVF + deriving (Show) instance Pretty SemanticLog where - pretty theLog = case theLog of - LogShake shakeLog -> pretty shakeLog - LogNoAST path -> "no HieAst exist for file" <> pretty path - LogNoVF -> "no VirtualSourceFile exist for file" - + pretty theLog = case theLog of + LogShake shakeLog -> pretty shakeLog + LogNoAST path -> "no HieAst exist for file" <> pretty path + LogNoVF -> "no VirtualSourceFile exist for file" diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs index 65de02e578..fb29c14729 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Utils.hs @@ -5,16 +5,12 @@ module Ide.Plugin.SemanticTokens.Utils where -import qualified Data.Array as A import Data.ByteString (ByteString) import Data.ByteString.Char8 (unpack) import qualified Data.Map as Map import Development.IDE (Position (..), Range (..)) import Development.IDE.GHC.Compat -import qualified Development.IDE.GHC.Compat as Compat import Ide.Plugin.SemanticTokens.Types -import Language.LSP.VFS (CodePointPosition (..), - CodePointRange (..)) import Prelude hiding (span) deriving instance Show DeclType diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 234cd88fb8..9781e7e5c9 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -185,8 +185,8 @@ semanticTokensFunctionTests = "get semantic of functions" [ goldenWithSemanticTokens "functions" "TFunction", goldenWithSemanticTokens "local functions" "TFunctionLocal", - goldenWithSemanticTokens "function in let binding" "TFunctionLet" - , goldenWithSemanticTokens "negative case non-function with constraint" "TNoneFunctionWithConstraint" + goldenWithSemanticTokens "function in let binding" "TFunctionLet", + goldenWithSemanticTokens "negative case non-function with constraint" "TNoneFunctionWithConstraint" ] main :: IO () From 2d5f6fa16a3977898166d39549d3650dc186f423 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 5 Jan 2024 23:15:09 +0800 Subject: [PATCH 71/73] turn semantic tokens off by default --- hls-plugin-api/src/Ide/Types.hs | 2 +- .../src/Ide/Plugin/SemanticTokens.hs | 11 ++++++----- plugins/hls-semantic-tokens-plugin/test/Main.hs | 14 +++++++++++++- .../schema/ghc92/default-config.golden.json | 3 +++ .../ghc92/vscode-extension-schema.golden.json | 6 ++++++ .../schema/ghc94/default-config.golden.json | 3 +++ .../ghc94/vscode-extension-schema.golden.json | 6 ++++++ .../schema/ghc96/default-config.golden.json | 3 +++ .../ghc96/vscode-extension-schema.golden.json | 6 ++++++ .../schema/ghc98/default-config.golden.json | 3 +++ .../ghc98/vscode-extension-schema.golden.json | 6 ++++++ 11 files changed, 56 insertions(+), 7 deletions(-) diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index 51ae0426bd..d2cfc70d9e 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -518,7 +518,7 @@ instance PluginMethod Request Method_TextDocumentRangeFormatting where pid = pluginId pluginDesc instance PluginMethod Request Method_TextDocumentSemanticTokensFull where - handlesRequest = pluginEnabledWithFeature plcCallHierarchyOn + handlesRequest = pluginEnabledWithFeature plcSemanticTokensOn instance PluginMethod Request Method_TextDocumentPrepareCallHierarchy where handlesRequest = pluginEnabledWithFeature plcCallHierarchyOn diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs index 2ca8453a2b..2386827a2a 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens.hs @@ -11,9 +11,10 @@ import Language.LSP.Protocol.Message descriptor :: Recorder (WithPriority SemanticLog) -> PluginId -> PluginDescriptor IdeState descriptor recorder plId = (defaultPluginDescriptor plId "Provides semantic tokens") - { Ide.Types.pluginHandlers = - mkPluginHandler SMethod_TextDocumentSemanticTokensFull Internal.semanticTokensFull, - Ide.Types.pluginRules = - Internal.getSemanticTokensRule recorder - <> Internal.persistentGetSemanticTokensRule + { Ide.Types.pluginHandlers = mkPluginHandler SMethod_TextDocumentSemanticTokensFull Internal.semanticTokensFull, + Ide.Types.pluginRules = Internal.getSemanticTokensRule recorder <> Internal.persistentGetSemanticTokensRule, + pluginConfigDescriptor = + defaultConfigDescriptor + { configInitialGenericConfig = (configInitialGenericConfig defaultConfigDescriptor) {plcGlobalOn = False} + } } diff --git a/plugins/hls-semantic-tokens-plugin/test/Main.hs b/plugins/hls-semantic-tokens-plugin/test/Main.hs index 9781e7e5c9..56a8f47393 100644 --- a/plugins/hls-semantic-tokens-plugin/test/Main.hs +++ b/plugins/hls-semantic-tokens-plugin/test/Main.hs @@ -67,7 +67,19 @@ mkFs :: [FS.FileTree] -> FS.VirtualFileTree mkFs = FS.mkVirtualFileTree testDataDir semanticTokensPlugin :: Test.Hls.PluginTestDescriptor SemanticLog -semanticTokensPlugin = Test.Hls.mkPluginTestDescriptor Ide.Plugin.SemanticTokens.descriptor "SemanticTokens" +semanticTokensPlugin = Test.Hls.mkPluginTestDescriptor enabledSemanticDescriptor "SemanticTokens" + where + enabledSemanticDescriptor recorder plId = + let semanticDescriptor = Ide.Plugin.SemanticTokens.descriptor recorder plId + in semanticDescriptor + { pluginConfigDescriptor = + (pluginConfigDescriptor semanticDescriptor) + { configInitialGenericConfig = + (configInitialGenericConfig (pluginConfigDescriptor semanticDescriptor)) + { plcGlobalOn = True + } + } + } mkSemanticTokensParams :: TextDocumentIdentifier -> SemanticTokensParams mkSemanticTokensParams = SemanticTokensParams Nothing Nothing diff --git a/test/testdata/schema/ghc92/default-config.golden.json b/test/testdata/schema/ghc92/default-config.golden.json index d78d49e046..949df9ed88 100644 --- a/test/testdata/schema/ghc92/default-config.golden.json +++ b/test/testdata/schema/ghc92/default-config.golden.json @@ -114,6 +114,9 @@ "retrie": { "globalOn": true }, + "semanticTokens": { + "globalOn": false + }, "splice": { "globalOn": true } diff --git a/test/testdata/schema/ghc92/vscode-extension-schema.golden.json b/test/testdata/schema/ghc92/vscode-extension-schema.golden.json index 690de92ab5..d7e3623ed1 100644 --- a/test/testdata/schema/ghc92/vscode-extension-schema.golden.json +++ b/test/testdata/schema/ghc92/vscode-extension-schema.golden.json @@ -249,6 +249,12 @@ "scope": "resource", "type": "boolean" }, + "haskell.plugin.semanticTokens.globalOn": { + "default": true, + "description": "Enables semanticTokens plugin", + "scope": "resource", + "type": "boolean" + }, "haskell.plugin.splice.globalOn": { "default": true, "description": "Enables splice plugin", diff --git a/test/testdata/schema/ghc94/default-config.golden.json b/test/testdata/schema/ghc94/default-config.golden.json index 50efb986c2..96f2567cec 100644 --- a/test/testdata/schema/ghc94/default-config.golden.json +++ b/test/testdata/schema/ghc94/default-config.golden.json @@ -114,6 +114,9 @@ "retrie": { "globalOn": true }, + "semanticTokens": { + "globalOn": false + }, "splice": { "globalOn": true }, diff --git a/test/testdata/schema/ghc94/vscode-extension-schema.golden.json b/test/testdata/schema/ghc94/vscode-extension-schema.golden.json index 30c843b3d6..f9e00d2f18 100644 --- a/test/testdata/schema/ghc94/vscode-extension-schema.golden.json +++ b/test/testdata/schema/ghc94/vscode-extension-schema.golden.json @@ -249,6 +249,12 @@ "scope": "resource", "type": "boolean" }, + "haskell.plugin.semanticTokens.globalOn": { + "default": true, + "description": "Enables semanticTokens plugin", + "scope": "resource", + "type": "boolean" + }, "haskell.plugin.splice.globalOn": { "default": true, "description": "Enables splice plugin", diff --git a/test/testdata/schema/ghc96/default-config.golden.json b/test/testdata/schema/ghc96/default-config.golden.json index 50efb986c2..96f2567cec 100644 --- a/test/testdata/schema/ghc96/default-config.golden.json +++ b/test/testdata/schema/ghc96/default-config.golden.json @@ -114,6 +114,9 @@ "retrie": { "globalOn": true }, + "semanticTokens": { + "globalOn": false + }, "splice": { "globalOn": true }, diff --git a/test/testdata/schema/ghc96/vscode-extension-schema.golden.json b/test/testdata/schema/ghc96/vscode-extension-schema.golden.json index 30c843b3d6..f9e00d2f18 100644 --- a/test/testdata/schema/ghc96/vscode-extension-schema.golden.json +++ b/test/testdata/schema/ghc96/vscode-extension-schema.golden.json @@ -249,6 +249,12 @@ "scope": "resource", "type": "boolean" }, + "haskell.plugin.semanticTokens.globalOn": { + "default": true, + "description": "Enables semanticTokens plugin", + "scope": "resource", + "type": "boolean" + }, "haskell.plugin.splice.globalOn": { "default": true, "description": "Enables splice plugin", diff --git a/test/testdata/schema/ghc98/default-config.golden.json b/test/testdata/schema/ghc98/default-config.golden.json index 8ad95561c6..31c5a79400 100644 --- a/test/testdata/schema/ghc98/default-config.golden.json +++ b/test/testdata/schema/ghc98/default-config.golden.json @@ -79,6 +79,9 @@ "qualifyImportedNames": { "globalOn": true }, + "semanticTokens": { + "globalOn": false + }, "stan": { "globalOn": false } diff --git a/test/testdata/schema/ghc98/vscode-extension-schema.golden.json b/test/testdata/schema/ghc98/vscode-extension-schema.golden.json index 5950032867..5073a3e339 100644 --- a/test/testdata/schema/ghc98/vscode-extension-schema.golden.json +++ b/test/testdata/schema/ghc98/vscode-extension-schema.golden.json @@ -171,6 +171,12 @@ "scope": "resource", "type": "boolean" }, + "haskell.plugin.semanticTokens.globalOn": { + "default": true, + "description": "Enables semanticTokens plugin", + "scope": "resource", + "type": "boolean" + }, "haskell.plugin.stan.globalOn": { "default": true, "description": "Enables stan plugin", From 4bc7c4e21d695b173cc7a1cd239163f471ad712a Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 5 Jan 2024 23:58:05 +0800 Subject: [PATCH 72/73] fix doc --- .../src/Ide/Plugin/SemanticTokens/Mappings.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs index 80ea885447..b369b0403c 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Mappings.hs @@ -100,6 +100,7 @@ hieKindFunMasksKind hieKind = case hieKind of -- wz1000 offered -- the idea from https://gitlab.haskell.org/ghc/haddock/-/blob/b0b0e0366457c9aefebcc94df74e5de4d00e17b7/haddock-api/src/Haddock/Backends/Hyperlinker/Utils.hs#L107 +-- optimize version of looking for which types are functions without unfolding the whole type recoverFunMaskArray :: -- | flat types A.Array TypeIndex HieTypeFlat -> @@ -108,7 +109,7 @@ recoverFunMaskArray :: recoverFunMaskArray flattened = unflattened where -- The recursion in 'unflattened' is crucial - it's what gives us sharing - -- between the elements of the array. + -- function indicator check. unflattened :: A.Array TypeIndex Bool unflattened = fmap (\flatTy -> go (fmap (unflattened A.!) flatTy)) flattened From 65148ad208ccbfdc69eece47fbde4b0525539256 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 6 Jan 2024 14:21:17 +0800 Subject: [PATCH 73/73] clean up doc --- .../src/Ide/Plugin/SemanticTokens/Query.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs index 99884bb879..7758176d04 100644 --- a/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs +++ b/plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticTokens/Query.hs @@ -1,10 +1,9 @@ -{- - The query module is used to query the semantic tokens from the AST --} {-# LANGUAGE ExplicitNamespaces #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} +-- | +-- The query module is used to query the semantic tokens from the AST module Ide.Plugin.SemanticTokens.Query where import Data.Either (rights)