module Unison.Server.SearchResult where

import Data.Set qualified as Set
import Unison.HashQualified (HashQualified)
import Unison.HashQualifiedPrime qualified as HQ'
import Unison.Name (Name)
import Unison.Name qualified as Name
import Unison.Names (Names (..))
import Unison.Names qualified as Names
import Unison.Prelude
import Unison.Reference (Reference)
import Unison.Referent (Referent)
import Unison.Referent qualified as Referent
import Unison.Syntax.Name {- instance Name.Alphabetical Name -} ()
import Unison.Util.Relation qualified as R

data SearchResult = Tp TypeResult | Tm TermResult deriving (SearchResult -> SearchResult -> Bool
(SearchResult -> SearchResult -> Bool)
-> (SearchResult -> SearchResult -> Bool) -> Eq SearchResult
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SearchResult -> SearchResult -> Bool
== :: SearchResult -> SearchResult -> Bool
$c/= :: SearchResult -> SearchResult -> Bool
/= :: SearchResult -> SearchResult -> Bool
Eq, Eq SearchResult
Eq SearchResult =>
(SearchResult -> SearchResult -> Ordering)
-> (SearchResult -> SearchResult -> Bool)
-> (SearchResult -> SearchResult -> Bool)
-> (SearchResult -> SearchResult -> Bool)
-> (SearchResult -> SearchResult -> Bool)
-> (SearchResult -> SearchResult -> SearchResult)
-> (SearchResult -> SearchResult -> SearchResult)
-> Ord SearchResult
SearchResult -> SearchResult -> Bool
SearchResult -> SearchResult -> Ordering
SearchResult -> SearchResult -> SearchResult
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: SearchResult -> SearchResult -> Ordering
compare :: SearchResult -> SearchResult -> Ordering
$c< :: SearchResult -> SearchResult -> Bool
< :: SearchResult -> SearchResult -> Bool
$c<= :: SearchResult -> SearchResult -> Bool
<= :: SearchResult -> SearchResult -> Bool
$c> :: SearchResult -> SearchResult -> Bool
> :: SearchResult -> SearchResult -> Bool
$c>= :: SearchResult -> SearchResult -> Bool
>= :: SearchResult -> SearchResult -> Bool
$cmax :: SearchResult -> SearchResult -> SearchResult
max :: SearchResult -> SearchResult -> SearchResult
$cmin :: SearchResult -> SearchResult -> SearchResult
min :: SearchResult -> SearchResult -> SearchResult
Ord, Int -> SearchResult -> ShowS
[SearchResult] -> ShowS
SearchResult -> String
(Int -> SearchResult -> ShowS)
-> (SearchResult -> String)
-> ([SearchResult] -> ShowS)
-> Show SearchResult
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SearchResult -> ShowS
showsPrec :: Int -> SearchResult -> ShowS
$cshow :: SearchResult -> String
show :: SearchResult -> String
$cshowList :: [SearchResult] -> ShowS
showList :: [SearchResult] -> ShowS
Show)

data TermResult = TermResult
  { TermResult -> HashQualified Name
termName :: HashQualified Name,
    TermResult -> Referent
referent :: Referent,
    TermResult -> Set (HashQualified Name)
termAliases :: Set (HQ'.HashQualified Name)
  }
  deriving (TermResult -> TermResult -> Bool
(TermResult -> TermResult -> Bool)
-> (TermResult -> TermResult -> Bool) -> Eq TermResult
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TermResult -> TermResult -> Bool
== :: TermResult -> TermResult -> Bool
$c/= :: TermResult -> TermResult -> Bool
/= :: TermResult -> TermResult -> Bool
Eq, Eq TermResult
Eq TermResult =>
(TermResult -> TermResult -> Ordering)
-> (TermResult -> TermResult -> Bool)
-> (TermResult -> TermResult -> Bool)
-> (TermResult -> TermResult -> Bool)
-> (TermResult -> TermResult -> Bool)
-> (TermResult -> TermResult -> TermResult)
-> (TermResult -> TermResult -> TermResult)
-> Ord TermResult
TermResult -> TermResult -> Bool
TermResult -> TermResult -> Ordering
TermResult -> TermResult -> TermResult
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: TermResult -> TermResult -> Ordering
compare :: TermResult -> TermResult -> Ordering
$c< :: TermResult -> TermResult -> Bool
< :: TermResult -> TermResult -> Bool
$c<= :: TermResult -> TermResult -> Bool
<= :: TermResult -> TermResult -> Bool
$c> :: TermResult -> TermResult -> Bool
> :: TermResult -> TermResult -> Bool
$c>= :: TermResult -> TermResult -> Bool
>= :: TermResult -> TermResult -> Bool
$cmax :: TermResult -> TermResult -> TermResult
max :: TermResult -> TermResult -> TermResult
$cmin :: TermResult -> TermResult -> TermResult
min :: TermResult -> TermResult -> TermResult
Ord, Int -> TermResult -> ShowS
[TermResult] -> ShowS
TermResult -> String
(Int -> TermResult -> ShowS)
-> (TermResult -> String)
-> ([TermResult] -> ShowS)
-> Show TermResult
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TermResult -> ShowS
showsPrec :: Int -> TermResult -> ShowS
$cshow :: TermResult -> String
show :: TermResult -> String
$cshowList :: [TermResult] -> ShowS
showList :: [TermResult] -> ShowS
Show)

data TypeResult = TypeResult
  { TypeResult -> HashQualified Name
typeName :: HashQualified Name,
    TypeResult -> Reference
reference :: Reference,
    TypeResult -> Set (HashQualified Name)
typeAliases :: Set (HQ'.HashQualified Name)
  }
  deriving (TypeResult -> TypeResult -> Bool
(TypeResult -> TypeResult -> Bool)
-> (TypeResult -> TypeResult -> Bool) -> Eq TypeResult
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TypeResult -> TypeResult -> Bool
== :: TypeResult -> TypeResult -> Bool
$c/= :: TypeResult -> TypeResult -> Bool
/= :: TypeResult -> TypeResult -> Bool
Eq, Eq TypeResult
Eq TypeResult =>
(TypeResult -> TypeResult -> Ordering)
-> (TypeResult -> TypeResult -> Bool)
-> (TypeResult -> TypeResult -> Bool)
-> (TypeResult -> TypeResult -> Bool)
-> (TypeResult -> TypeResult -> Bool)
-> (TypeResult -> TypeResult -> TypeResult)
-> (TypeResult -> TypeResult -> TypeResult)
-> Ord TypeResult
TypeResult -> TypeResult -> Bool
TypeResult -> TypeResult -> Ordering
TypeResult -> TypeResult -> TypeResult
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: TypeResult -> TypeResult -> Ordering
compare :: TypeResult -> TypeResult -> Ordering
$c< :: TypeResult -> TypeResult -> Bool
< :: TypeResult -> TypeResult -> Bool
$c<= :: TypeResult -> TypeResult -> Bool
<= :: TypeResult -> TypeResult -> Bool
$c> :: TypeResult -> TypeResult -> Bool
> :: TypeResult -> TypeResult -> Bool
$c>= :: TypeResult -> TypeResult -> Bool
>= :: TypeResult -> TypeResult -> Bool
$cmax :: TypeResult -> TypeResult -> TypeResult
max :: TypeResult -> TypeResult -> TypeResult
$cmin :: TypeResult -> TypeResult -> TypeResult
min :: TypeResult -> TypeResult -> TypeResult
Ord, Int -> TypeResult -> ShowS
[TypeResult] -> ShowS
TypeResult -> String
(Int -> TypeResult -> ShowS)
-> (TypeResult -> String)
-> ([TypeResult] -> ShowS)
-> Show TypeResult
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TypeResult -> ShowS
showsPrec :: Int -> TypeResult -> ShowS
$cshow :: TypeResult -> String
show :: TypeResult -> String
$cshowList :: [TypeResult] -> ShowS
showList :: [TypeResult] -> ShowS
Show)

pattern Tm' :: HashQualified Name -> Referent -> Set (HQ'.HashQualified Name) -> SearchResult
pattern $mTm' :: forall {r}.
SearchResult
-> (HashQualified Name
    -> Referent -> Set (HashQualified Name) -> r)
-> ((# #) -> r)
-> r
$bTm' :: HashQualified Name
-> Referent -> Set (HashQualified Name) -> SearchResult
Tm' hq r as = Tm (TermResult hq r as)

pattern Tp' :: HashQualified Name -> Reference -> Set (HQ'.HashQualified Name) -> SearchResult
pattern $mTp' :: forall {r}.
SearchResult
-> (HashQualified Name
    -> Reference -> Set (HashQualified Name) -> r)
-> ((# #) -> r)
-> r
$bTp' :: HashQualified Name
-> Reference -> Set (HashQualified Name) -> SearchResult
Tp' hq r as = Tp (TypeResult hq r as)

-- | Construct a term search result from a primary name, referent, and set of aliases.
termResult ::
  HashQualified Name -> Referent -> Set (HQ'.HashQualified Name) -> SearchResult
termResult :: HashQualified Name
-> Referent -> Set (HashQualified Name) -> SearchResult
termResult HashQualified Name
hq Referent
r Set (HashQualified Name)
as = TermResult -> SearchResult
Tm (HashQualified Name
-> Referent -> Set (HashQualified Name) -> TermResult
TermResult HashQualified Name
hq Referent
r Set (HashQualified Name)
as)

termSearchResult :: Names -> Name -> Referent -> SearchResult
termSearchResult :: Names -> Name -> Referent -> SearchResult
termSearchResult Names
b Name
n Referent
r =
  HashQualified Name
-> Referent -> Set (HashQualified Name) -> SearchResult
termResult (HashQualified Name -> HashQualified Name
forall n. HashQualified n -> HashQualified n
HQ'.toHQ (Names -> Name -> Referent -> HashQualified Name
Names._hqTermName Names
b Name
n Referent
r)) Referent
r (Names -> Name -> Referent -> Set (HashQualified Name)
Names._hqTermAliases Names
b Name
n Referent
r)

-- | Construct a type search result from a primary name, reference, and set of aliases.
typeResult ::
  HashQualified Name -> Reference -> Set (HQ'.HashQualified Name) -> SearchResult
typeResult :: HashQualified Name
-> Reference -> Set (HashQualified Name) -> SearchResult
typeResult HashQualified Name
hq Reference
r Set (HashQualified Name)
as = TypeResult -> SearchResult
Tp (HashQualified Name
-> Reference -> Set (HashQualified Name) -> TypeResult
TypeResult HashQualified Name
hq Reference
r Set (HashQualified Name)
as)

typeSearchResult :: Names -> Name -> Reference -> SearchResult
typeSearchResult :: Names -> Name -> Reference -> SearchResult
typeSearchResult Names
b Name
n Reference
r =
  HashQualified Name
-> Reference -> Set (HashQualified Name) -> SearchResult
typeResult (HashQualified Name -> HashQualified Name
forall n. HashQualified n -> HashQualified n
HQ'.toHQ (Names -> Name -> Reference -> HashQualified Name
Names._hqTypeName Names
b Name
n Reference
r)) Reference
r (Names -> Name -> Reference -> Set (HashQualified Name)
Names._hqTypeAliases Names
b Name
n Reference
r)

name :: SearchResult -> HashQualified Name
name :: SearchResult -> HashQualified Name
name = \case
  Tm TermResult
t -> TermResult -> HashQualified Name
termName TermResult
t
  Tp TypeResult
t -> TypeResult -> HashQualified Name
typeName TypeResult
t

aliases :: SearchResult -> Set (HQ'.HashQualified Name)
aliases :: SearchResult -> Set (HashQualified Name)
aliases = \case
  Tm TermResult
t -> TermResult -> Set (HashQualified Name)
termAliases TermResult
t
  Tp TypeResult
t -> TypeResult -> Set (HashQualified Name)
typeAliases TypeResult
t

-- | TypeResults yield a `Referent.Ref`
toReferent :: SearchResult -> Referent
toReferent :: SearchResult -> Referent
toReferent (Tm (TermResult HashQualified Name
_ Referent
r Set (HashQualified Name)
_)) = Referent
r
toReferent (Tp (TypeResult HashQualified Name
_ Reference
r Set (HashQualified Name)
_)) = Reference -> Referent
Referent.Ref Reference
r

truncateAliases :: Int -> SearchResult -> SearchResult
truncateAliases :: Int -> SearchResult -> SearchResult
truncateAliases Int
n = \case
  Tm (TermResult HashQualified Name
hq Referent
r Set (HashQualified Name)
as) -> HashQualified Name
-> Referent -> Set (HashQualified Name) -> SearchResult
termResult HashQualified Name
hq Referent
r ((HashQualified Name -> HashQualified Name)
-> Set (HashQualified Name) -> Set (HashQualified Name)
forall b a. Ord b => (a -> b) -> Set a -> Set b
Set.map (Int -> HashQualified Name -> HashQualified Name
forall n. Int -> HashQualified n -> HashQualified n
HQ'.take Int
n) Set (HashQualified Name)
as)
  Tp (TypeResult HashQualified Name
hq Reference
r Set (HashQualified Name)
as) -> HashQualified Name
-> Reference -> Set (HashQualified Name) -> SearchResult
typeResult HashQualified Name
hq Reference
r ((HashQualified Name -> HashQualified Name)
-> Set (HashQualified Name) -> Set (HashQualified Name)
forall b a. Ord b => (a -> b) -> Set a -> Set b
Set.map (Int -> HashQualified Name -> HashQualified Name
forall n. Int -> HashQualified n -> HashQualified n
HQ'.take Int
n) Set (HashQualified Name)
as)

-- | You may want to sort this list differently afterward.
fromNames :: Names -> [SearchResult]
fromNames :: Names -> [SearchResult]
fromNames Names
b =
  ((Name, Reference) -> SearchResult)
-> [(Name, Reference)] -> [SearchResult]
forall a b. (a -> b) -> [a] -> [b]
map ((Name -> Reference -> SearchResult)
-> (Name, Reference) -> SearchResult
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (Names -> Name -> Reference -> SearchResult
typeSearchResult Names
b)) (Relation Name Reference -> [(Name, Reference)]
forall a b. Relation a b -> [(a, b)]
R.toList (Relation Name Reference -> [(Name, Reference)])
-> (Names -> Relation Name Reference)
-> Names
-> [(Name, Reference)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Names -> Relation Name Reference
Names.types (Names -> [(Name, Reference)]) -> Names -> [(Name, Reference)]
forall a b. (a -> b) -> a -> b
$ Names
b)
    [SearchResult] -> [SearchResult] -> [SearchResult]
forall a. Semigroup a => a -> a -> a
<> ((Name, Referent) -> SearchResult)
-> [(Name, Referent)] -> [SearchResult]
forall a b. (a -> b) -> [a] -> [b]
map ((Name -> Referent -> SearchResult)
-> (Name, Referent) -> SearchResult
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (Names -> Name -> Referent -> SearchResult
termSearchResult Names
b)) (Relation Name Referent -> [(Name, Referent)]
forall a b. Relation a b -> [(a, b)]
R.toList (Relation Name Referent -> [(Name, Referent)])
-> (Names -> Relation Name Referent) -> Names -> [(Name, Referent)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Names -> Relation Name Referent
Names.terms (Names -> [(Name, Referent)]) -> Names -> [(Name, Referent)]
forall a b. (a -> b) -> a -> b
$ Names
b)

_fromNames :: Names -> [SearchResult]
_fromNames :: Names -> [SearchResult]
_fromNames n0 :: Names
n0@(Names Relation Name Referent
terms Relation Name Reference
types) = [SearchResult]
typeResults [SearchResult] -> [SearchResult] -> [SearchResult]
forall a. Semigroup a => a -> a -> a
<> [SearchResult]
termResults
  where
    typeResults :: [SearchResult]
typeResults =
      [ Names -> Name -> Reference -> SearchResult
typeSearchResult Names
n0 Name
name Reference
r
        | (Name
name, Reference
r) <- Relation Name Reference -> [(Name, Reference)]
forall a b. Relation a b -> [(a, b)]
R.toList Relation Name Reference
types
      ]
    termResults :: [SearchResult]
termResults =
      [ Names -> Name -> Referent -> SearchResult
termSearchResult Names
n0 Name
name Referent
r
        | (Name
name, Referent
r) <- Relation Name Referent -> [(Name, Referent)]
forall a b. Relation a b -> [(a, b)]
R.toList Relation Name Referent
terms
      ]

-- | Sort a list of search results by name. If names are equal, fall back to comparing by reference (putting types
-- before terms).
compareByName :: SearchResult -> SearchResult -> Ordering
compareByName :: SearchResult -> SearchResult -> Ordering
compareByName SearchResult
x SearchResult
y =
  Ordering
primary Ordering -> Ordering -> Ordering
forall a. Semigroup a => a -> a -> a
<> Ordering
secondary
  where
    primary :: Ordering
primary = HashQualified Name -> HashQualified Name -> Ordering
forall n. Alphabetical n => n -> n -> Ordering
Name.compareAlphabetical (SearchResult -> HashQualified Name
name SearchResult
x) (SearchResult -> HashQualified Name
name SearchResult
y)
    secondary :: Ordering
secondary =
      case (SearchResult
x, SearchResult
y) of
        (Tp TypeResult
_, Tm TermResult
_) -> Ordering
LT
        (Tm TermResult
_, Tp TypeResult
_) -> Ordering
GT
        (Tp TypeResult
t0, Tp TypeResult
t1) -> Reference -> Reference -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (TypeResult -> Reference
reference TypeResult
t0) (TypeResult -> Reference
reference TypeResult
t1)
        (Tm TermResult
t0, Tm TermResult
t1) -> Referent -> Referent -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (TermResult -> Referent
referent TermResult
t0) (TermResult -> Referent
referent TermResult
t1)