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.
  -}
  
  {-# LANGUAGE TypeApplications #-}
  
  module Glean.Glass.Query
    (
    -- * Files
      srcFile
    , fileInfo
    , fileDigests
  
    -- * Working with XRefs
    , fileEntityLocations
    , fileEntityXRefsGenEntities
  
    -- * Finding references to declarations
    , findReferenceRangeSpan
    , findReferenceRangeSpanByDef
  
    -- * Finding references for call hierarchy
    , findReferenceEntities
    , findReferenceEntitiesFast
  
    -- * Finding source definitions for generated entities
    , generatedEntityToIdlEntity
  
    -- * offsets and conversions to lines
    , fileLines
  
    -- * Entity annotations
    , symbolKind
  
    -- * Query helpers
    , entityLocation
  
    , symbolToEntity
  
    ) where
  
  import Data.Text (Text)
  
  import qualified Glean
  import Glean.Angle as Angle
  
  import Glean.Glass.Base (GleanPath(..))
  
  import qualified Glean.Schema.CodemarkupTypes.Types as Code
  import qualified Glean.Schema.Codemarkup.Types as Code
  import qualified Glean.Schema.Code.Types as Code
  import qualified Glean.Schema.Src.Types as Src
  import qualified Glean.Schema.Glass.Types as Glass
  import qualified Glean.Schema.Digest.Types as Digest
  
  --
  -- Find the id in this db of a source file. We mostly operate on these ids once
  -- we have them, to avoid passing strings around.
  --
  -- > src.File "www/flib/intern/glean/Glean.php"
  --
  srcFile :: GleanPath -> Angle Src.File
  srcFile (GleanPath path) =
    predicate @Src.File $
      string path
  
  -- For a potential filepath, fetch all file metadata
  fileInfo :: GleanPath -> Angle Glass.FileInfo
  fileInfo (GleanPath path) = predicate @Glass.FileInfo $
    rec $
      field @"file" (string path)
    end
  
  -- | Given a file id, look up the index of line endings
  --
  -- Line ending tables are needed to do line:col conversions, but we want to
  -- minimize when we have to do this conversion. The Range vs Location types in
  -- the .thrift API capture when this is needed.
  --
  fileLines :: Glean.IdOf Src.File -> Angle Src.FileLines
  fileLines fileid =
    predicate @Src.FileLines (
      rec $ field @"file"
        (asPredicate (factId fileid))
      end)
  
  --
  -- Bulk fetch all digests for a set of filepath facts. Non-recursive, as the
  -- Digest type will expand. Extremely cheap
  --
  -- > digest.FileDigest { file = [$371971 : src.File, $61059][..] }
  --
  fileDigests :: [Glean.IdOf Src.File] -> Angle Digest.FileDigest
  fileDigests fileIds =
    predicate @Digest.FileDigest (
      rec $ field @"file"
        (asPredicate (elementsOf (factIdsArray fileIds)))
      end)
  
  -- | Get the definition entities defined in this file, and their declaration
  -- sites.
  --
  -- > www> src.File "www/flib/intern/glean/Glean.php"
  -- { "id": 123968960, "key": "www/flib/intern/glean/Glean.php" }
  --
  -- > {L , E} where
  -- >  codemarkup.FileEntityLocations {$123968960, L, E}
  --
  fileEntityLocations :: Glean.IdOf Src.File -> Angle (Code.Location, Code.Entity)
  fileEntityLocations fileid =
    vars $ \(location :: Angle Code.Location) (entity :: Angle Code.Entity) ->
      tuple (location,entity) `where_` [
        wild .= predicate @Code.FileEntityLocations (
          rec $
            field @"file" (asPredicate (factId fileid)) $
            field @"location" location $
            field @"entity" entity
          end)
        ]
  
  -- | Find all "generic" xrefs from this file, regular and xlang
  fileEntityXRefsGenEntities
   :: Glean.IdOf Src.File
   -> Bool
   -> Angle Code.GenericEntity
  fileEntityXRefsGenEntities fileid includeXRefs =
     vars $ \(genEntity :: Angle Code.GenericEntity) ->
      genEntity `where_` (
        stmt (predicate @Code.FileXRefsGenericEntities (
          rec $
            field @"file" (asPredicate (factId fileid)) $
            field @"genEntity" genEntity
          end)) : [genEntity .= alt @"plainEntity" wild | not includeXRefs])
  
  -- | Entity-based find-references returning native range or bytespan
  findReferenceRangeSpan
    :: Angle Code.Entity
    -> Angle (Src.File, Code.RangeSpan)
  findReferenceRangeSpan ent =
    vars $ \(reffile :: Angle Src.File) (rangespan :: Angle Code.RangeSpan) ->
      Angle.tuple (reffile, rangespan) `where_` [
        wild .= predicate @Code.EntityReferences (
        rec $
            field @"target" ent $
            field @"file" (asPredicate reffile) $
            field @"range" rangespan
          end
        )
      ]
  
  -- | Entity-based find-references returning native range or bytespan filtered by
  -- the referenced symbols being defined in the given file.
  findReferenceRangeSpanByDef
    :: Angle Code.Entity
    -> Text
    -> Angle (Src.File, Code.RangeSpan)
  findReferenceRangeSpanByDef ent filename =
    vars $ \(reffile :: Angle Src.File)
      (rangespan :: Angle Code.RangeSpan)
      (deffile :: Angle Src.File)
      (xref :: Angle Code.XRefLocation)
      (targetLoc :: Angle Code.Location) ->
        Angle.tuple (reffile, rangespan) `where_` [
          wild .= predicate @Code.EntityReferences (
            rec $
                field @"target" ent $
                field @"file" (asPredicate reffile) $
                field @"range" rangespan
              end
            )
          , deffile .= predicate @Src.File (string filename)
          , wild .= predicate @Code.FileEntityXRefLocations (
              rec $
                field @"file" (asPredicate reffile) $
                field @"xref" xref $
                field @"entity" ent
              end
            )
          , xref .= rec (
              field @"target" targetLoc $
              field @"source" rangespan
            end)
          , targetLoc .= rec (
              field @"file" (asPredicate deffile)
            end)
        ]
  
  generatedEntityToIdlEntity
    :: Angle Code.Entity
    -> Angle (Code.Entity, Src.File)
  generatedEntityToIdlEntity entity =
    vars $ \(idlEntity :: Angle Code.Entity) (file :: Angle Src.File) ->
      tuple (idlEntity, file) `where_` [
        wild .=
          predicate @Code.GeneratedEntityToIdlEntity (
            rec $
              field @"entity" entity $
              field @"idlEntity" (rec $
                  field @"entity" (just idlEntity) $
                  field @"file" (asPredicate file)
                end)
            end)
      ]
  
  -- | Entity-based find-references for call hierarchy.
  --   Returns referencing entities with their location, and the call site
  --   Beware: O(NM) on the number of referencing entites
  --           and the xrefs in their source files
  findReferenceEntities
    :: Angle Code.Entity
    -> Angle (Src.File, Code.Entity, Code.Location, Code.RangeSpan)
  findReferenceEntities ent =
    vars $ \(reffile :: Angle Src.File)
            (caller :: Angle Code.Entity)
            (referenceRangespan :: Angle Code.RangeSpan)
            (callerLocation :: Angle Code.Location)
            ->
      Angle.tuple (reffile, caller, callerLocation, referenceRangespan) `where_`
      [ wild .= predicate @Code.ReferencingEntity (
        rec $
            field @"target" ent $
            field @"referrer" caller $
            field @"reference_file" (asPredicate reffile) $
            field @"reference_range" referenceRangespan $
            field @"referrer_location" callerLocation
          end
        )
      , wild .= predicate @Code.FileEntityKinds (
          rec $
            field @"entity" caller $
            field @"file" (asPredicate reffile) $
            field @"kind"
              (enum Code.SymbolKind_Constructor .|
               enum Code.SymbolKind_Function .|
               enum Code.SymbolKind_Macro .|
               enum Code.SymbolKind_Method
              )
          end
      )
      ]
  
  -- | Like 'findReferenceEntities' but faster
  --   Available only for fbsource and www.hack
  --   Does not return call site locations
  findReferenceEntitiesFast
    :: Angle Code.Entity -> Angle (Code.Entity, Code.Location)
  findReferenceEntitiesFast target =
    vars $ \(caller :: Angle Code.Entity) (location :: Angle Code.Location) ->
      Angle.tuple (caller, location) `where_`
        [ wild .= predicate @Code.EntitySource (rec $
            field @"target" target $
            field @"source" caller
            end)
        , wild .= predicate @Code.EntityLocation (rec $
            field @"entity" caller $
            field @"location" location
            end)
        ]
  
  -- | Given an entity find the kind associated with it
  symbolKind :: Angle Code.Entity -> Angle Code.SymbolKind
  symbolKind ent = var $ \kind -> kind `where_`
    [ wild .= predicate @Code.EntityKind (
        rec $
            field @"entity" ent $
            field @"kind" kind
        end)
    ]
  
  -- | Helper to generate entity location footers on entity search queries
  -- Takes free file and rangespan variables to bind
  entityLocation
    :: Angle Code.Entity
    -> (Angle Src.File -> Angle Code.RangeSpan -> Angle Text -> AngleStatement)
  entityLocation entity file rangespan name =
    wild .= predicate @Code.EntityLocation (
      rec $
        field @"entity" entity $
        field @"location" (
          rec $
            field @"name" name $
            field @"file" (asPredicate file) $
            field @"location" rangespan
          end)
      end)
  
  symbolToEntity :: Angle Code.SymbolId -> Angle (Code.SymbolId, Code.Entity)
  symbolToEntity symbol =
    vars $ \(entity :: Angle Code.Entity) ->
      tuple (symbol, entity) `where_` [
        wild .= predicate @Code.SymbolToEntity (
          rec $
            field @"symbol" symbol $
            field @"entity" entity
          end)
      ]
  