content: {-
    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.
  -}
  
  module Glean.Glass.Describe (
    -- * symbol information API
      mkSymbolDescription,
      mkBriefSymbolDescription,
      describeEntity,
      briefDescribeEntity
    ) where
  
  import Data.Maybe ( listToMaybe )
  import Data.Either.Extra ( eitherToMaybe )
  
  import qualified Glean
  import Glean.Haxl.Repos as Glean ( RepoHaxl )
  
  import Glean.Glass.Range ( rangeSpanToLocationRange )
  import Glean.Glass.Types
  import Glean.Glass.SymbolId ( toQualifiedName, entityLanguage, nativeSymbol )
  import Glean.Glass.SymbolKind ( findSymbolKind )
  import Glean.Glass.Search ( CodeEntityLocation(..) )
  import Glean.Glass.SymbolSig ( toSymbolSignatureText )
  import Glean.Glass.Annotations ( getAnnotationsForEntity )
  import Glean.Glass.Visibility ( getInfoForEntity )
  import Glean.Glass.Comments ( getCommentsForEntity )
  import Glean.Glass.Repos ( ScmRevisions, getRepoHashForLocation )
  import Glean.Glass.Utils ( eThrow, fst4)
  import Glean.Glass.Pretty.Cxx as Cxx (Qualified(..))
  import qualified Glean.Glass.SearchRelated as Search
  
  import qualified Glean.Schema.Code.Types as Code
  import qualified Glean.Glass.Types as Glass
  
  -- Helper to fill out symbol description metadata uniformly
  mkSymbolDescription
    :: SymbolId
    -> ScmRevisions
    -> RepoName
    -> CodeEntityLocation
    -> Maybe SymbolContext
    -> Glean.RepoHaxl u w SymbolDescription
  mkSymbolDescription  = mkDescription describeEntity
  
  -- Helper to fill out _basic_ symbol description metadata uniformly
  mkBriefSymbolDescription
    :: SymbolId
    -> ScmRevisions
    -> RepoName
    -> CodeEntityLocation
    -> Maybe SymbolContext
    -> Glean.RepoHaxl u w SymbolBasicDescription
  mkBriefSymbolDescription = mkDescription (const briefDescribeEntity)
  
  --- Worker to fill out symbol description metadata uniformly
  mkDescription
    :: (ScmRevisions -> Code.Entity -> SymbolResult -> Glean.RepoHaxl u w t)
    -> SymbolId
    -> ScmRevisions
    -> RepoName
    -> CodeEntityLocation
    -> Maybe SymbolContext
    -> Glean.RepoHaxl u w t
  mkDescription fn symbolId scmRevs repo CodeEntityLocation{..} mctx = do
    range <- rangeSpanToLocationRange repo entityFile entityRange
    kind <- eitherToMaybe <$> findSymbolKind entity
    qname <- eThrow =<< toQualifiedName entity -- non-optional now
    let lang = entityLanguage entity
        score = mempty
    fn scmRevs entity $
      SymbolResult symbolId range lang kind entityName score qname mctx
  
  -- | Return a brief description with only required fields set.
  -- Far more efficient for cases where we don't need everything
  briefDescribeEntity
    :: Code.Entity -> SymbolResult -> Glean.RepoHaxl u w SymbolBasicDescription
  briefDescribeEntity ent SymbolResult{..} = do
    symbolBasicDescription_signature <- fst <$> toSymbolSignatureText ent repo
      symbolResult_symbol Cxx.Qualified
    pure SymbolBasicDescription{..}
    where
      symbolBasicDescription_sym = symbolResult_symbol
      symbolBasicDescription_name = symbolResult_qname
      symbolBasicDescription_kind = symbolResult_kind
      symbolBasicDescription_language = symbolResult_language
      repo = locationRange_repository symbolResult_location
  
  -- | Return a description for a single Entity with a unique location.
  describeEntity
    :: ScmRevisions
    -> Code.Entity
    -> SymbolResult
    -> Glean.RepoHaxl u w SymbolDescription
  describeEntity scmRevs ent SymbolResult{..} = do
    symbolDescription_repo_hash <-
      getRepoHashForLocation symbolResult_location scmRevs <$> Glean.haxlRepo
    let symbolDescription_name = symbolResult_qname
    symbolDescription_annotations <- eThrow =<< getAnnotationsForEntity repo ent
    symbolDescription_pretty_comments <- eThrow =<< getCommentsForEntity repo ent
    (symbolDescription_visibility, symbolDescription_modifiers)
       <- eThrow =<< getInfoForEntity ent
    (symbolDescription_signature, symbolDescription_type_xrefs)
      <- toSymbolSignatureText ent repo symbolResult_symbol Cxx.Qualified
    symbolDescription_extends_relation <-
      relationDescription RelationType_Extends
    symbolDescription_contains_relation <-
      relationDescription RelationType_Contains
    symbolDescription_native_sym <- fmap Glass.NativeSymbol <$> nativeSymbol ent
    pure SymbolDescription{..}
    where
      symbolDescription_sym = symbolResult_symbol
      symbolDescription_kind = symbolResult_kind
      symbolDescription_language = symbolResult_language
  
      symbolDescription_sym_location = symbolResult_location
      symbolDescription_sym_other_locations = []
  
      repo = locationRange_repository symbolResult_location
  
      -- deprecated. we already have sym_location
      symbolDescription_location = SymbolPath {
        symbolPath_range = locationRange_range symbolResult_location,
        symbolPath_repository = locationRange_repository symbolResult_location,
        symbolPath_filepath = locationRange_filepath symbolResult_location
      }
  
      relationDescription relatedBy = do
        parents <- describeRelation RelationDirection_Parent
        children <- describeRelation RelationDirection_Child
  
        let firstParent = Search.parentRL <$> listToMaybe parents
            firstChild = Search.childRL <$> listToMaybe children
            relationDescription_firstParent = snd <$> firstParent
            relationDescription_firstChild = snd <$> firstChild
            relationDescription_hasMoreParents = length parents > 1
            relationDescription_hasMoreChildren = length children > 1
  
        relationDescription_firstParentName <- case firstParent of
          Nothing -> pure Nothing
          Just (p,_) -> eitherToMaybe <$> toQualifiedName (fst4 p)
        relationDescription_firstChildName <- case firstChild of
          Nothing -> pure Nothing
          Just (p,_) -> eitherToMaybe <$> toQualifiedName (fst4 p)
  
        pure RelationDescription{..}
        where
          describeRelation relation = Search.searchRelatedEntities 2
            Search.ShowAll Search.NotRecursive relation relatedBy ent repo
  