{- Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. This source code is licensed under the BSD-style license found in the LICENSE file in the root directory of this source tree. -} {-# OPTIONS_GHC -Wno-orphans #-} module Glean.Glass.SymbolId.LSIF ({- instances -}) where import Control.Monad.Catch ( throwM ) import Data.Maybe import Data.Text ( Text ) import qualified Data.Text as Text import qualified Network.URI.Encode as URI import Util.Text ( textShow ) import qualified Glean import qualified Glean.Haxl.Repos as Glean import qualified Glean.Schema.Lsif.Types as Lsif import qualified Glean.Schema.LsifTypes.Types as Lsif import Glean.Glass.Utils ( pathFragments ) import Glean.Glass.Types import Glean.Glass.SymbolId.Class instance Symbol Lsif.SomeEntity where toSymbol e = case e of Lsif.SomeEntity_defn defn -> toSymbolPredicate defn Lsif.SomeEntity_decl decl -> toSymbolPredicate decl _ -> throwM $ SymbolError "Unknown LSIF case" instance Symbol Lsif.DefinitionMoniker_key where toSymbol (Lsif.DefinitionMoniker_key defn (Just moniker)) = do Lsif.Moniker_key kind _scheme ident <- Glean.keyOf moniker case kind of -- case 1: locals. case a) no id, just position or b) id + pos Lsif.MonikerKind_Local -> Glean.keyOf defn >>= localsIdent -- case 2: non-locals w/ monikers _ -> do -- use the "lsif" tag to indicate symbol is a moniker only i <- Glean.keyOf ident return ("lsif" : pathFragments (URI.decodeText i)) -- case 3: no monikers at all, use path + global ident and hope for best toSymbol (Lsif.DefinitionMoniker_key defn Nothing) = toSymbolPredicate defn instance Symbol Lsif.Definition_key where toSymbol (Lsif.Definition_key doc range) = pathAndMaybeName doc range instance Symbol Lsif.Declaration_key where toSymbol (Lsif.Declaration_key doc range) = pathAndMaybeName doc range localsIdent :: Lsif.Definition_key -> Glean.RepoHaxl u m [Text] localsIdent (Lsif.Definition_key doc range) = pathAndLocalPosition doc range pathAndLocalPosition :: Lsif.Document -> Lsif.Range -> Glean.RepoHaxl u m [Text] pathAndLocalPosition doc range = do path <- filePathOfDocument doc mident <- identOfRange range pos <- positionOfRange range return (path ++ [""] ++ maybeToList mident ++ pos) pathAndMaybeName :: Lsif.Document -> Lsif.Range -> Glean.RepoHaxl u m [Text] pathAndMaybeName doc range = do path <- filePathOfDocument doc mident <- identOfRange range case mident of Just ident -> return (path ++ [ident]) Nothing -> do pos <- positionOfRange range return (path ++ [""] ++ pos) filePathOfDocument :: Lsif.Document -> Glean.RepoHaxl u m [Text] filePathOfDocument doc = do Lsif.Document_key{..} <- Glean.keyOf doc path <- Glean.keyOf document_key_file return (pathFragments path) identOfRange :: Lsif.Range -> Glean.RepoHaxl u m (Maybe Text) identOfRange range = do Lsif.Range_key{..} <- Glean.keyOf range ident <- Glean.keyOf range_key_text return $ if Text.null ident then Nothing else Just ident positionOfRange :: Lsif.Range -> Glean.RepoHaxl u m [Text] positionOfRange range = do Lsif.Range_key{..} <- Glean.keyOf range pure $ showSpan range_key_range showSpan :: Lsif.RangeSpan -> [Text] showSpan Lsif.RangeSpan{..} = map textShow [Glean.unNat rangeSpan_lineBegin, Glean.unNat rangeSpan_columnBegin ,Glean.unNat rangeSpan_lineEnd, Glean.unNat rangeSpan_columnEnd + 1] instance ToQName Lsif.SomeEntity where toQName e = case e of Lsif.SomeEntity_defn defn -> Glean.keyOf defn >>= toQName Lsif.SomeEntity_decl decl -> Glean.keyOf decl >>= toQName _ -> throwM $ SymbolError "Unknown LSIF case" instance ToQName Lsif.DefinitionMoniker_key where toQName (Lsif.DefinitionMoniker_key defn Nothing) = Glean.keyOf defn >>= toQName toQName (Lsif.DefinitionMoniker_key defn (Just moniker)) = do Lsif.Moniker_key kind _scheme ident <- Glean.keyOf moniker base <- symbolName defn fullContainer <- case kind of Lsif.MonikerKind_Local -> "" -- can't use moniker for locals namespace _ -> Glean.keyOf ident let container = fromMaybe fullContainer (Text.stripSuffix (":" <> base) fullContainer) return $ Right (Name base, Name container) instance ToQName Lsif.Definition_key where toQName (Lsif.Definition_key _ range) = do mid <- identOfRange range return $ Right (Name "", Name (fromMaybe "" mid)) instance ToQName Lsif.Declaration_key where toQName (Lsif.Declaration_key _ range) = do mid <- identOfRange range return $ Right (Name "", Name (fromMaybe "" mid)) symbolName :: Lsif.Definition -> Glean.RepoHaxl u m Text symbolName defn = do Lsif.Definition_key _ range <- Glean.keyOf defn mid <- identOfRange range pure (fromMaybe "" mid)