Merge pull request #316 from unisonweb/topic/hashqualified-name

Support for hash-qualified names in `list` and `view`
This commit is contained in:
Paul Chiusano 2019-02-05 10:14:35 -05:00 committed by GitHub
commit 10b1af16e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 606 additions and 1010 deletions

2
.gitignore vendored
View File

@ -1,5 +1,5 @@
# Unison
.unison/
.unison*/
.unisonHistory
# Haskell

View File

@ -3,9 +3,11 @@
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Unison.Builtin where
import Control.Arrow ( first )
import Control.Applicative ( liftA2 )
import qualified Data.Map as Map
import Data.Set ( Set )
import qualified Data.Set as Set
@ -30,9 +32,9 @@ import qualified Unison.TypeParser as TypeParser
import qualified Unison.Util.ColorText as Color
import Unison.Var ( Var )
import qualified Unison.Var as Var
import Unison.Names ( Names
, Name
)
import Unison.Name ( Name )
import qualified Unison.Name as Name
import Unison.Names ( Names )
import qualified Unison.Names as Names
import qualified Unison.Typechecker.TypeLookup as TL
import qualified Unison.Util.Relation as Rel
@ -101,13 +103,13 @@ builtinTerms =
(r, typ) <- Map.toList builtins0 ]
builtinTypesV :: Var v => [(v, R.Reference)]
builtinTypesV = first (Var.named) <$> builtinTypes
builtinTypesV = first (Name.toVar) <$> builtinTypes
builtinTypeNames :: Set Name
builtinTypeNames = Set.fromList (map fst builtinTypes)
builtinTypes :: [(Name, R.Reference)]
builtinTypes = (,) <*> R.Builtin <$>
builtinTypes = liftA2 (,) Name.unsafeFromText R.Builtin <$>
["Int", "Nat", "Float", "Boolean", "Sequence", "Text", "Stream", "Effect"]
-- | parse some builtin data types, and resolve their free variables using

View File

@ -28,8 +28,6 @@ import Data.Maybe ( catMaybes
)
import Data.Set ( Set )
import qualified Data.Set as Set
import Data.String ( fromString )
import qualified Data.Text as Text
import Data.Text ( Text )
import Data.Traversable ( for )
import Text.EditDistance ( defaultEditCosts
@ -42,7 +40,10 @@ import qualified Unison.Codebase.Branch as Branch
import qualified Unison.Codebase.TermEdit as TermEdit
import Unison.Codebase.TermEdit ( TermEdit )
import qualified Unison.DataDeclaration as DD
import Unison.Names ( Name )
import Unison.HashQualified ( HashQualified )
import qualified Unison.HashQualified as HQ
import Unison.Name ( Name )
import qualified Unison.Name as Name
import Unison.Parser ( Ann )
import qualified Unison.PrettyPrintEnv as PPE
import Unison.Reference ( Reference )
@ -75,6 +76,7 @@ type DataDeclaration v a = DD.DataDeclaration' v a
type EffectDeclaration v a = DD.EffectDeclaration' v a
type Term v a = Term.AnnotatedTerm v a
type Type v a = Type.AnnotatedType v a
type BranchName = Text
data Codebase m v a =
Codebase { getTerm :: Reference.Id -> m (Maybe (Term v a))
@ -83,12 +85,12 @@ data Codebase m v a =
, getTypeDeclaration :: Reference.Id -> m (Maybe (Decl v a))
, putTypeDeclarationImpl :: Reference.Id -> Decl v a -> m ()
, branches :: m [Name]
, getBranch :: Name -> m (Maybe Branch)
, branches :: m [BranchName]
, getBranch :: BranchName -> m (Maybe Branch)
-- thought: this merges the given branch with the existing branch
-- or creates a new branch if there's no branch with that name
, mergeBranch :: Name -> Branch -> m Branch
, branchUpdates :: m (m (), m (Set Name))
, mergeBranch :: BranchName -> Branch -> m Branch
, branchUpdates :: m (m (), m (Set BranchName))
, dependentsImpl :: Reference -> m (Set Reference.Id)
, builtinLoc :: a
@ -126,19 +128,18 @@ typecheckingEnvironment code t = do
Right d -> (Map.insert r d datas, effects)
pure $ TL.TypeLookup termTypes datas effects
fuzzyFindTerms' :: Branch -> [String] -> [(Text, Referent)]
fuzzyFindTerms' :: Branch -> [String] -> [(HashQualified, Referent)]
fuzzyFindTerms' (Branch.head -> branch) query =
let
termNames = Text.unpack <$> toList (Branch.allTermNames branch)
matchingTerms :: [String]
terms = Branch.allTerms branch
termNames = [(HQ.toString n, (n,r))
| r <- toList terms
, n <- toList (Branch.hashNamesForTerm r branch)]
matchingTerms :: [(String,(HashQualified,Referent))]
matchingTerms = if null query
then termNames
else query >>= \q -> sortedApproximateMatches q termNames
refsForName :: String -> [Referent]
refsForName (Text.pack -> name) = Set.toList $ Branch.termsNamed name branch
makePair (Text.pack -> name) r =
(Branch.hashQualifiedTermName branch name r, r)
in matchingTerms >>= \name -> makePair name <$> refsForName name
else query >>= \q -> sortedApproximateMatches' q termNames
in snd <$> matchingTerms
fuzzyFindTermTypes
:: forall m v a
@ -146,7 +147,7 @@ fuzzyFindTermTypes
=> Codebase m v a
-> Branch
-> [String]
-> m [(Text, Referent, Maybe (Type v a))]
-> m [(HashQualified, Referent, Maybe (Type v a))]
fuzzyFindTermTypes codebase branch query =
let found = fuzzyFindTerms' branch query
tripleForRef name ref = (name, ref, ) <$> case ref of
@ -155,17 +156,16 @@ fuzzyFindTermTypes codebase branch query =
Referent.Con r cid -> getTypeOfConstructor codebase r cid
in traverse (uncurry tripleForRef) found
fuzzyFindTypes' :: Branch -> [String] -> [(Text, Reference)]
fuzzyFindTypes' :: Branch -> [String] -> [(HashQualified, Reference)]
fuzzyFindTypes' (Branch.head -> branch) query =
let
typeNames =
Text.unpack <$> toList (Branch.allTypeNames branch)
typeNames = toList (Branch.allTypeNames branch)
matchingTypes = if null query
then typeNames
else query >>= \q -> sortedApproximateMatches q typeNames
refsForName (Text.pack -> name) = Set.toList $ Branch.typesNamed name branch
makePair (Text.pack -> name) r =
(Branch.hashQualifiedTypeName branch name r, r)
else query >>= \q -> asStrings (sortedApproximateMatches q) typeNames
asStrings f names = Name.fromString <$> f (Name.toString <$> names)
refsForName name = Set.toList $ Branch.typesNamed name branch
makePair name r = (Branch.hashQualifiedTypeName branch name r, r)
in matchingTypes >>= \name -> makePair name <$> refsForName name
prettyTypeSource :: (Monad m, Var v) => Codebase m v a -> Name -> Reference -> Branch -> m (Maybe (Pretty ColorText))
@ -176,18 +176,20 @@ listReferencesMatching
:: (Var v, Monad m) => Codebase m v a -> Branch -> [String] -> m String
listReferencesMatching code (Branch.head -> b) query = do
let
termNames = Text.unpack <$> toList (Branch.allTermNames b)
typeNames = Text.unpack <$> toList (Branch.allTypeNames b)
termNames = toList (Branch.allTermNames b)
typeNames = toList (Branch.allTypeNames b)
matchingTerms = if null query
then termNames
else query >>= \q -> sortedApproximateMatches q termNames
else query >>= \q -> asStrings (sortedApproximateMatches q) termNames
matchingTypes = if null query
then typeNames
else query >>= \q -> sortedApproximateMatches q typeNames
else query >>= \q -> asStrings (sortedApproximateMatches q) typeNames
matchingTypeRefs = matchingTypes
>>= \name -> Set.toList (Branch.typesNamed (Text.pack name) b)
>>= \name -> Set.toList (Branch.typesNamed name b)
matchingTermRefs = matchingTerms
>>= \name -> Set.toList (Branch.termsNamed (Text.pack name) b)
>>= \name -> Set.toList (Branch.termsNamed name b)
asStrings f names = Name.fromString <$> f (Name.toString <$> names)
listReferences code
b
(matchingTypeRefs ++ [ r | Ref r <- matchingTermRefs ])
@ -195,7 +197,7 @@ listReferencesMatching code (Branch.head -> b) query = do
listReferences
:: (Var v, Monad m) => Codebase m v a -> Branch0 -> [Reference] -> m String
listReferences code branch refs = do
let ppe = Branch.prettyPrintEnv1 branch
let ppe = Branch.prettyPrintEnv branch
terms0 <- forM refs $ \r -> do
otyp <- getTypeOfTerm code r
pure $ (PPE.termName ppe (Referent.Ref r), otyp)
@ -245,7 +247,7 @@ initialize c = do
prettyBinding
:: (Var.Var v, Monad m)
=> Codebase m v a
-> Name
-> HashQualified
-> Referent
-> Branch0
-> m (Maybe (Pretty String))
@ -256,56 +258,26 @@ prettyBinding cb name r0@(Referent.Ref r1@(Reference.DerivedId r)) b =
go Nothing = pure Nothing
go (Just tm) =
let
-- We boost the `(r0,name)` association since if this is a recursive
-- We force the `(r0,name)` association since if this is a recursive
-- fn whose body also mentions `r`, want name to be the same as the binding.
ppEnv = Branch.prettyPrintEnv [b]
`mappend` PPE.scale 10 (PPE.fromTermNames [(r0, name)])
ppEnv = PPE.assignTermName r0 name $ Branch.prettyPrintEnv b
in case tm of
Term.Ann' _ _ ->
pure $ Just (TermPrinter.prettyBinding ppEnv (Var.named name) tm)
pure $ Just (TermPrinter.prettyBinding ppEnv name tm)
_ -> do
Just typ <- getTypeOfTerm cb r1
pure . Just $ TermPrinter.prettyBinding
ppEnv
(Var.named name)
name
(Term.ann (ABT.annotation tm) tm typ)
prettyBinding _ _ r _ = error $ "unpossible " ++ show r
prettyBindings :: (Var.Var v, Monad m)
=> Codebase m v a -> [(Name,Referent)] -> Branch0 -> m (Pretty String)
=> Codebase m v a -> [(HashQualified,Referent)] -> Branch0 -> m (Pretty String)
prettyBindings cb tms b = do
ds <- catMaybes <$> (forM tms $ \(name,r) -> prettyBinding cb name r b)
pure $ PP.linesSpaced ds
-- Search for and display bindings matching the given query
prettyBindingsQ
:: (Var.Var v, Monad m)
=> Codebase m v a
-> String
-> Branch0
-> m (Pretty String)
prettyBindingsQ cb query b =
let possible = Branch.allTermNames b
matches =
sortedApproximateMatches query (Text.unpack <$> toList possible)
str = fromString
bs =
[ (name, r)
| name <- Text.pack <$> matches
, r <- take 1 (toList $ Branch.termsNamed name b)
]
go pp = if length matches > 5
then PP.linesSpaced
[ pp
, "... "
<> str (show (length matches - 5))
<> " more (use `> list "
<> str query
<> "` to see all matches)"
]
else pp
in go <$> prettyBindings cb (take 5 bs) b
prettyListingQ
:: (Var.Var v, Monad m)
=> Codebase m v a
@ -373,7 +345,7 @@ makeSelfContained
-> m (UF.UnisonFile v a)
makeSelfContained code b (UF.UnisonFile datas0 effects0 tm) = do
deps <- foldM (transitiveDependencies code) Set.empty (Term.dependencies tm)
let pp = Branch.prettyPrintEnv1 b
let pp = Branch.prettyPrintEnv b
termName r = PPE.termName pp (Referent.Ref r)
typeName r = PPE.typeName pp r
decls <- fmap catMaybes . forM (toList deps) $ \case
@ -381,18 +353,18 @@ makeSelfContained code b (UF.UnisonFile datas0 effects0 tm) = do
_ -> pure Nothing
termsByRef <- fmap catMaybes . forM (toList deps) $ \case
r@(Reference.DerivedId rid) ->
fmap (r, Var.named (termName r), ) <$> getTerm code rid
fmap (r, HQ.toVar (termName r), ) <$> getTerm code rid
_ -> pure Nothing
let
unref t = ABT.visitPure go t
where
go t@(Term.Ref' (r@(Reference.DerivedId _))) =
Just (Term.var (ABT.annotation t) (Var.named $ termName r))
Just (Term.var (ABT.annotation t) (HQ.toVar $ termName r))
go _ = Nothing
datas = Map.fromList
[ (v, (r, dd)) | (r, Right dd) <- decls, v <- [Var.named (typeName r)] ]
[ (v, (r, dd)) | (r, Right dd) <- decls, v <- [HQ.toVar (typeName r)] ]
effects = Map.fromList
[ (v, (r, ed)) | (r, Left ed) <- decls, v <- [Var.named (typeName r)] ]
[ (v, (r, ed)) | (r, Left ed) <- decls, v <- [HQ.toVar (typeName r)] ]
bindings = [ ((ABT.annotation t, v), unref t) | (_, v, t) <- termsByRef ]
unrefBindings bs = [ (av, unref t) | (av, t) <- bs ]
tm' = case tm of
@ -423,7 +395,30 @@ sortedApproximateMatches q possible = trim (sortOn fst matches)
trim ((_, h) : _) | h == q = [h]
trim ms = map snd $ takeWhile (\(n, _) -> n - 7 < nq `div` 4) ms
branchExists :: Functor m => Codebase m v a -> Name -> m Bool
sortedApproximateMatches' :: String -> [(String,a)] -> [(String,a)]
sortedApproximateMatches' q possible = trim (sortOn fst matches)
where
nq = length q
score (s,_)
| s == q = 0 :: Int
| -- exact match is top choice
map toLower q == map toLower s = 1
| -- ignore case
q `isSuffixOf` s = 2
| -- matching suffix is pretty good
q `isInfixOf` s = 3
| -- a match somewhere
q `isPrefixOf` s = 4
| -- ...
map toLower q `isInfixOf` map toLower s = 5
| q `isSubsequenceOf` s = 6
| otherwise = 7 + editDistance (map toLower q) (map toLower s)
editDistance q s = levenshteinDistance defaultEditCosts q s
matches = map (\s -> (score s, s)) possible
trim ((_, (h,a)) : _) | h == q = [(h,a)]
trim ms = map snd $ takeWhile (\(n, _) -> n - 7 < nq `div` 4) ms
branchExists :: Functor m => Codebase m v a -> BranchName -> m Bool
branchExists codebase name = elem name <$> branches codebase
builtinBranch :: Branch
@ -459,7 +454,7 @@ isTerm code = fmap isJust . getTypeOfTerm code
isType :: Applicative m => Codebase m v a -> Reference -> m Bool
isType c r = case r of
Reference.Builtin b -> pure (b `Set.member` Builtin.builtinTypeNames)
Reference.Builtin b -> pure (Name.unsafeFromText b `Set.member` Builtin.builtinTypeNames)
Reference.DerivedId r -> isJust <$> getTypeDeclaration c r
_ -> error "impossible"
@ -497,7 +492,7 @@ unhashComponent
-> m (Maybe (Map v (Reference, Term v a, Type v a)))
unhashComponent code b ref = do
let component = Reference.members $ Reference.componentFor ref
ppe = Branch.prettyPrintEnv1 b
ppe = Branch.prettyPrintEnv b
isTerm <- isTerm code ref
isType <- isType code ref
if isTerm then do
@ -510,7 +505,7 @@ unhashComponent code b ref = do
Reference.DerivedId id -> do
mtm <- getTerm code id
tm <- maybe (fail $ "Missing term with id " <> show id) pure mtm
pure (Var.named $ PPE.termName ppe (Referent.Ref termRef), (termRef, tm, tp))
pure (HQ.toVar $ PPE.termName ppe (Referent.Ref termRef), (termRef, tm, tp))
_ -> fail $ "Cannot unhashComponent for a builtin: " ++ show termRef
unhash m =
let f (ref,_oldTm,oldTyp) (_ref,newTm) = (ref,newTm,oldTyp)

View File

@ -19,8 +19,6 @@ import Data.Map (Map)
import qualified Data.Map as Map
import Data.Set (Set)
import qualified Data.Set as Set
import qualified Data.Text as Text
import Data.Text (Text)
import Prelude hiding (head,subtract)
import Unison.Codebase.Causal (Causal)
import qualified Unison.Codebase.Causal as Causal
@ -32,10 +30,13 @@ import qualified Unison.DataDeclaration as DD
import Unison.Hash (Hash)
import Unison.Hashable (Hashable)
import qualified Unison.Hashable as H
import Unison.Names (Name, Names (..))
import Unison.HashQualified (HashQualified)
import qualified Unison.HashQualified as HashQualified
import Unison.Name (Name)
import Unison.Names (Names (..))
import qualified Unison.Name as Name
import qualified Unison.Names as Names
import Unison.Reference (Reference)
import qualified Unison.Reference as Reference
import Unison.Referent (Referent)
import qualified Unison.Referent as Referent
import qualified Unison.UnisonFile as UF
@ -45,7 +46,6 @@ import Unison.Util.TransitiveClosure (transitiveClosure)
import Unison.PrettyPrintEnv (PrettyPrintEnv)
import qualified Unison.PrettyPrintEnv as PPE
import Unison.Var (Var)
import qualified Unison.Var as Var
-- todo:
-- probably should refactor Reference to include info about whether it
@ -261,15 +261,22 @@ instance Monoid Branch0 where
mempty = Branch0 mempty mempty R.empty R.empty
mappend = (<>)
allNames :: Branch0 -> Set Name
allNames = Set.union <$> allTermNames <*> allTypeNames
allNamesHashQualified :: Branch0 -> Set HashQualified
allNamesHashQualified b =
Set.union (allTermsHashQualified b) (allTypesHashQualified b)
allTermNames :: Branch0 -> Set Name
allTermNames = R.dom . termNamespace
allTermsHashQualified :: Branch0 -> Set HashQualified
allTermsHashQualified b = foldMap (\r -> hashNamesForTerm r b) (allTerms b)
allTypeNames :: Branch0 -> Set Name
allTypeNames b0 = R.dom (typeNamespace b0)
allTypesHashQualified :: Branch0 -> Set HashQualified
allTypesHashQualified b = foldMap (\r -> hashNamesForType r b) (allTypes b)
hasTermNamed :: Name -> Branch0 -> Bool
hasTermNamed n b = not . null $ termsNamed n b
@ -286,7 +293,10 @@ typesNamed :: Name -> Branch0 -> Set Reference
typesNamed name = R.lookupDom name . typeNamespace
namesForTerm :: Referent -> Branch0 -> Set Name
namesForTerm ref b = let
namesForTerm ref = R.lookupRan ref . termNamespace
hashNamesForTerm :: Referent -> Branch0 -> Set HashQualified
hashNamesForTerm ref b = let
ns = (termNamespace b) :: Relation Name Referent
hashLen = numHashChars b
names = (R.lookupRan ref ns) :: Set Name
@ -296,7 +306,10 @@ namesForTerm ref b = let
in Set.map f names
namesForType :: Reference -> Branch0 -> Set Name
namesForType ref b = let
namesForType ref = R.lookupRan ref . typeNamespace
hashNamesForType :: Reference -> Branch0 -> Set HashQualified
hashNamesForType ref b = let
ns = (typeNamespace b) :: Relation Name Reference
hashLen = numHashChars b
names = (R.lookupRan ref ns) :: Set Name
@ -305,60 +318,66 @@ namesForType ref b = let
$ hashQualifyTypeName hashLen n (R.lookupDom n references)
in Set.map f names
hashQualifyTermName :: Int -> Name -> Set Referent -> Map Referent Text
hashQualifyTermName :: Int -> Name -> Set Referent -> Map Referent HashQualified
hashQualifyTermName numHashChars n rs =
if Set.size rs < 2 then Map.fromList [(r, n) | r <- toList rs ]
else Map.fromList [ (r, n <> Text.pack (Referent.showShort numHashChars r))
if Set.size rs < 2
then Map.fromList [(r, HashQualified.fromName n) | r <- toList rs ]
else Map.fromList [ (r, HashQualified.forReferent r numHashChars n)
| r <- toList rs ]
hashQualifyTypeName :: Int -> Name -> Set Reference -> Map Reference Text
hashQualifyTypeName :: Int -> Name -> Set Reference -> Map Reference HashQualified
hashQualifyTypeName numHashChars n rs =
if Set.size rs < 2 then Map.fromList [(r, n) | r <- toList rs ]
else Map.fromList [ (r, n <> Text.pack (Reference.showShort numHashChars r))
if Set.size rs < 2
then Map.fromList [(r, HashQualified.fromName n) | r <- toList rs ]
else Map.fromList [ (r, HashQualified.forReference r numHashChars n)
| r <- toList rs ]
-- Get the appropriately hash-qualified version of a name for term.
-- Should be the same as the input name if the branch is unconflicted.
hashQualifiedTermName :: Branch0 -> Name -> Referent -> Text
hashQualifiedTermName :: Branch0 -> Name -> Referent -> HashQualified
hashQualifiedTermName b n r =
if (> 1) . length . R.lookupDom n . termNamespace $ b then
-- name is conflicted
n <> Text.pack (Referent.showShort (numHashChars b) r)
else n
HashQualified.forReferent r (numHashChars b) n
else HashQualified.fromName n
hashQualifiedTypeName :: Branch0 -> Name -> Reference -> Text
hashQualifiedTypeName :: Branch0 -> Name -> Reference -> HashQualified
hashQualifiedTypeName b n r =
if (> 1) . length . R.lookupDom n . typeNamespace $ b then
-- name is conflicted
n <> Text.pack (Reference.showShort (numHashChars b) r)
else n
HashQualified.forReference r (numHashChars b) n
else HashQualified.fromName n
oldNamesForTerm :: Int -> Referent -> Branch0 -> Set Name
oldNamesForTerm :: Int -> Referent -> Branch0 -> Set HashQualified
oldNamesForTerm numHashChars ref
= Set.map (<> Text.pack (Referent.showShort numHashChars ref))
= Set.map (HashQualified.forReferent ref numHashChars)
. R.lookupRan ref
. (view $ oldNamespaceL . terms)
oldNamesForType :: Int -> Reference -> Branch0 -> Set Name
oldNamesForType :: Int -> Reference -> Branch0 -> Set HashQualified
oldNamesForType numHashChars ref
= Set.map (<> Text.pack (Reference.showShort numHashChars ref))
= Set.map (HashQualified.forReference ref numHashChars)
. R.lookupRan ref
. (view $ oldNamespaceL . types)
numHashChars :: Branch0 -> Int
numHashChars = const 3 -- todo: use trie to find depth of branching
prettyPrintEnv1 :: Branch0 -> PrettyPrintEnv
prettyPrintEnv1 b = PPE.PrettyPrintEnv terms types where
-- We must choose a canonical name for each referent in the branch.
-- In the future we might like a way for the user to choose a preferred name
-- (i.e. just `unionLeft` the user preferences before the arbitrary choice)
prettyPrintEnv :: Branch0 -> PrettyPrintEnv
prettyPrintEnv b = PPE.PrettyPrintEnv terms types where
hashLen = numHashChars b
or :: Set a -> Set a -> Set a
or s1 s2 = if Set.null s1 then s2 else s1
terms r = multiset $ namesForTerm r b `or` oldNamesForTerm hashLen r b
types r = multiset $ namesForType r b `or` oldNamesForType hashLen r b
multiset ks = Map.fromList [ (k, 1) | k <- Set.toList ks ]
terms r =
Set.lookupMin $ hashNamesForTerm r b `or` oldNamesForTerm hashLen r b
types r =
Set.lookupMin $ hashNamesForType r b `or` oldNamesForType hashLen r b
prettyPrintEnv :: [Branch0] -> PrettyPrintEnv
prettyPrintEnv = foldMap prettyPrintEnv1
-- prettyPrintEnv :: [Branch0] -> PrettyPrintEnv
-- prettyPrintEnv = foldMap prettyPrintEnv1
before :: Branch -> Branch -> Bool
before b b2 = unbranch b `Causal.before` unbranch b2
@ -547,7 +566,7 @@ fromTypecheckedFile
:: forall v a . Var v => UF.TypecheckedUnisonFile v a -> Branch0
fromTypecheckedFile file =
let
toName = Var.name
toName = Name.unsafeFromVar
hashedTerms = UF.hashTerms file
ctors :: [(v, Referent)]
ctors = Map.toList $ UF.hashConstructors file
@ -590,8 +609,8 @@ intersectWithFile branch file =
>>= (>>= (\(v, _, _) -> if Set.member v termNames then [v] else []))
)
where
typeNames = Set.map (Var.named) $ allTypeNames branch
termNames = Set.map (Var.named) $ allTermNames branch
typeNames = Set.map (Name.toVar) $ allTypeNames branch
termNames = Set.map (Name.toVar) $ allTermNames branch
modify :: (Branch0 -> Branch0) -> Branch -> Branch

View File

@ -1,538 +0,0 @@
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE DoAndIfThenElse #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Unison.Codebase.CommandLine (main) where
import Data.Bifunctor ( second )
import System.Random ( randomRIO )
import Control.Concurrent ( forkIO )
import Control.Exception ( catch
, finally
)
import Control.Monad ( forM_
, forever
, liftM2
, void
, when
)
import Control.Monad.STM ( STM
, atomically
)
import qualified Data.Char as Char
import Data.Foldable ( toList
, traverse_
)
import Data.IORef ( IORef
, newIORef
, writeIORef
, readIORef
)
import Data.List ( find
, isSuffixOf
, sort
)
import Data.Set ( Set )
import qualified Data.Set as Set
import Data.String ( fromString )
import Data.Strings ( strPadLeft )
import Data.Text ( Text
, pack
, unpack
)
import qualified Data.Text.IO
import qualified System.Console.ANSI as Console
import System.FilePath ( FilePath )
import qualified Text.Read as Read
import qualified Unison.Reference as Reference
import System.IO.Error ( isEOFError )
import qualified Unison.Builtin as B
import Unison.Codebase ( Codebase )
import qualified Unison.Codebase as Codebase
import Unison.Codebase.Branch ( Branch )
import qualified Unison.Codebase.Branch as Branch
import Unison.Names ( Name )
import Unison.Codebase.Runtime ( Runtime )
import qualified Unison.Codebase.Runtime as RT
import qualified Unison.Codebase.Watch as Watch
import Unison.FileParsers ( parseAndSynthesizeFile )
import qualified Unison.Parser as Parser
import qualified Unison.PrintError as PrintError
import Unison.PrintError ( prettyParseError
, prettyTypecheckedFile
, renderNoteAsANSI
)
import Unison.Result ( pattern Result )
import qualified Unison.Result as Result
import qualified Unison.UnisonFile as UF
import qualified Unison.Util.ColorText as Color
import qualified Unison.Util.Menu as Menu
import Unison.Util.Monoid
import qualified Unison.Util.Pretty as PP
import Unison.Util.TQueue ( TQueue )
import qualified Unison.Util.TQueue as TQueue
import Unison.Var ( Var )
import qualified Unison.Var as Var
import qualified Data.Map as Map
import Unison.Parser ( Ann )
import qualified Data.Text as Text
import Unison.Names ( Names )
import qualified Unison.Term as Term
data Event
= UnisonFileChanged FilePath Text
| UnisonBranchChanged (Set Name)
| EOF
allow :: FilePath -> Bool
allow = liftM2 (||) (".u" `isSuffixOf`) (".uu" `isSuffixOf`)
data CreateCancel = Create | Cancel deriving Show
main
:: forall v
. Var v
=> FilePath
-> Name
-> Maybe FilePath
-> IO (Runtime v)
-> Codebase IO v Ann
-> IO ()
main dir currentBranchName initialFile startRuntime codebase = do
queue <- TQueue.newIO
lineQueue <- TQueue.newIO
runtime <- startRuntime
lastTypechecked <- newIORef
(Nothing, UF.typecheckedUnisonFile0, mempty)
let takeActualLine = atomically (takeLine lineQueue)
-- load initial unison file if specified
case initialFile of
Just file | allow file -> do
text <- Data.Text.IO.readFile file
atomically . TQueue.enqueue queue $ UnisonFileChanged file text
_ -> pure ()
-- enqueue stdin into lineQueue
void
. forkIO
. (`catch` eofHandler queue)
. forever
$ getChar
>>= atomically
. TQueue.enqueue lineQueue
-- watch for .u file changes
void . forkIO $ do
(_, watcher) <- Watch.watchDirectory dir allow
forever $ do
(filePath, text) <- watcher
atomically . TQueue.enqueue queue $ UnisonFileChanged filePath text
-- watch for external branch changes
(cancelExternalBranchUpdates, externalBranchUpdates) <- Codebase.branchUpdates
codebase
void . forkIO . forever $ do
updatedBranches <- externalBranchUpdates
atomically . TQueue.enqueue queue . UnisonBranchChanged $ updatedBranches
-- load current branch from disk
branch <- Codebase.getBranch codebase currentBranchName
(`finally` (RT.terminate runtime *> cancelExternalBranchUpdates))
$ case branch of
Nothing -> do
selectBranch codebase currentBranchName takeActualLine >>= \case
Just (name, branch) ->
go0 branch name queue lineQueue lastTypechecked runtime
Nothing -> putStrLn "Exiting."
Just b ->
go0 b currentBranchName queue lineQueue lastTypechecked runtime
where
eofHandler queue e =
if isEOFError e then (atomically . TQueue.enqueue queue) EOF else ioError e
go0
:: Branch
-> Name
-> TQueue Event
-> TQueue Char
-> IORef
( Maybe FilePath
, UF.TypecheckedUnisonFile v Parser.Ann
, PrintError.Env
)
-> Runtime v
-> IO ()
go0 branch branchName queue lineQueue lastTypechecked runtime = go
branch
branchName
where
clearLastTypechecked =
writeIORef lastTypechecked (Nothing, UF.typecheckedUnisonFile0, mempty)
-- print prompt and whatever input was on it / at it
printPrompt :: Name -> IO ()
printPrompt branchName = do
incompleteLine <- atomically . peekIncompleteLine $ lineQueue
putStr $ "\r" ++ unpack branchName ++ "> " ++ incompleteLine
handleUnisonFile :: Runtime v -> Names -> FilePath -> Text -> IO ()
handleUnisonFile runtime names filePath src = do
Result notes r <- Result.getResult $ parseAndSynthesizeFile
(pure . const B.typeLookup <> Codebase.typeLookupForDependencies codebase)
names
filePath
src
case r of
Nothing -> do -- parsing failed
Console.setTitle "Unison \128721"
forM_ notes $ \case
Result.Parsing err -> do
putStrLn . Color.toANSI $ prettyParseError (unpack src) err
clearLastTypechecked
err ->
error
$ "I was expecting a parsing error here but got:\n"
++ show err
Just (errorEnv, r) -> case r of
Nothing -> do -- typechecking failed
Console.setTitle "Unison \128721"
let showNote notes = intercalateMap
"\n\n"
(renderNoteAsANSI errorEnv (unpack src))
(filter notInfo notes)
notInfo (Result.TypeInfo _) = False
notInfo _ = True
putStrLn . showNote . toList $ notes
clearLastTypechecked
Just unisonFile -> do
Console.setTitle "Unison ✅"
let emoticons = "🌸🌺🌹🌻🌼🌷🌵🌴🍄🌲"
n <- randomRIO (0, length emoticons - 1)
let uf = UF.discardTerm unisonFile
defs = prettyTypecheckedFile uf errorEnv
prettyDefs = Color.toANSI defs
when (not $ null defs) . putStrLn
$ ""
++ [emoticons !! n]
++ " Found and typechecked the following definitions in "
++ filePath
++ ":\n"
writeIORef lastTypechecked (Just filePath, uf, errorEnv)
putStrLn prettyDefs
putStrLn
"👀 Now evaluating any watch expressions (lines starting with `>`) ...\n"
selfContainedFile <- Codebase.makeSelfContained codebase (Branch.head branch) $ UF.discardTypes' unisonFile
(watchExpressions, _term) <-
RT.evaluate runtime selfContainedFile codebase
uncurry (Watch.watchPrinter names) `traverse_` watchExpressions
go :: Branch -> Name -> IO ()
go branch name = do
printPrompt name
-- wait for new lines from user or asynchronous events from filesystem
TQueue.raceIO (TQueue.peek queue) (awaitCompleteLine lineQueue) >>= \case
Right _ -> processLine branch name
Left _ -> atomically (TQueue.dequeue queue) >>= \case
EOF -> putStrLn "^D"
UnisonFileChanged filePath text -> do
Console.setTitle "Unison"
Console.clearScreen
Console.setCursorPosition 0 0
let names = Branch.toNames branch
handleUnisonFile runtime names filePath text
go branch name
UnisonBranchChanged branches -> if Set.member name branches
then do
b' <- Codebase.getBranch codebase name
case b' of
Just b' -> do
when (branch /= b') $ do
putStrLn "I've merged some external changes to the branch."
putStrLn
$ "TODO: tell the user what changed as a result of the merge"
go b' name
Nothing -> do
putStrLn
$ "The current branch was deleted by some external process, "
++ "so I'm going to re-save what I have in memory."
branch' <- mergeBranchAndShowDiff codebase name branch
go branch' name
else go branch name
-- Looks at `lastTypechecked` for matching definitions and lets the user
-- add them to the codebase. Present the user with a menu if args doesn't
-- match what's in lastTypechecked.
addDefinitions :: Branch -> Name -> [String] -> IO ()
addDefinitions branch name args = case args of
_ -> readIORef lastTypechecked >>= \(filePath, typecheckedFile, env) ->
case filePath of
Nothing -> do
putStrLn
$ "Nothing to do. Editing a .u file in "
<> dir
<> " will tell me about new definitions."
go branch name
Just _ -> do
let branchUpdate = Branch.fromTypecheckedFile typecheckedFile
collisions = Branch.nameCollisions branchUpdate (Branch.head branch)
-- todo: collisions should really be collisions `Branch.subtract` branch,
-- since if the names have a matching hash that's fine
if collisions /= mempty
then do
putStrLn
$ "The following names collided with existing definitions:\n"
putStrLn $ intercalateMap
" "
Text.unpack
(toList $ Branch.allNames collisions)
putStrLn
"\nUse the `> edit` command to have these definitions replace the existing ones."
go branch name
else do
-- todo: this should probably just be a function in Codebase,
-- something like
-- addFile :: Codebase -> TypecheckedUnisonFile -> m ()
let hashedTerms = UF.hashTerms typecheckedFile
putStrLn $ "Adding the following definitions:"
putStrLn ""
putStrLn $ Color.toANSI
(prettyTypecheckedFile typecheckedFile env)
putStrLn ""
let
allTypeDecls =
(second Left <$> UF.effectDeclarations' typecheckedFile)
`Map.union` ( second Right
<$> UF.dataDeclarations' typecheckedFile
)
forM_ (Map.toList allTypeDecls)
$ \(v, (r@(Reference.DerivedId id), dd)) -> do
decl <- Codebase.getTypeDeclaration codebase id
case decl of
Nothing -> do
Codebase.putTypeDeclaration codebase id dd
Just _ ->
-- todo - can treat this as adding an alias
-- (same hash, but different name in this branch)
putStrLn
$ Var.nameStr v
++ " already exists with hash "
++ show r
++ ", skipping."
forM_ (Map.toList hashedTerms)
$ \(v, (r@(Reference.DerivedId id), tm, typ)) -> do
o <- Codebase.getTerm codebase id
case o of
Just _ ->
-- todo - can treat this as adding an alias
-- (same hash, but different name in this branch)
putStrLn
$ Var.nameStr v
++ " already exists with hash "
++ show r
++ ", skipping."
Nothing ->
-- Discard all line/column info when adding to the codebase
Codebase.putTerm
codebase
id
(Term.amap (const Parser.External) tm)
typ
branch <- mergeBranchAndShowDiff
codebase
name
(Branch.append branchUpdate branch)
let emoticons = "🌉🏙🌃🌁🌅🎆🌄🌠🌇"
n <- randomRIO (0, length emoticons - 1)
putStrLn
$ (emoticons !! n)
: " All done. You can view or edit any definition via `> view <name>`."
putStrLn ""
clearLastTypechecked
go branch name
viewDefinitions :: Branch -> Name -> [String] -> IO ()
viewDefinitions branch name args = do
prettys <- traverse (\q -> Codebase.prettyBindingsQ codebase q (Branch.head branch))
args
putStrLn . PP.render 80 $ PP.linesSpaced prettys
go branch name
processLine :: Branch -> Name -> IO ()
processLine branch name = do
let takeActualLine = atomically $ takeLine lineQueue
line <- takeActualLine
case words line of
"add" : args -> addDefinitions branch name args
"view" : args -> viewDefinitions branch name args
ls : args
| ls == "list" || -- todo: more comprehensive way of allowing command abbreviations
ls == "ls" ||
ls == "l"
-> do
out <- Codebase.listReferencesMatching codebase branch args
putStrLn out
putStrLn ""
go branch name
["branch"] -> do
branches <- sort <$> Codebase.branches codebase
forM_ branches $ \name' -> if name' == name
then putStrLn $ " * " ++ unpack name'
else putStrLn $ " " ++ unpack name'
-- idea: could instead prompt user and read directly from lineQueue to handle
go branch name
["branch", name'] -> do
branch' <- selectBranch codebase (pack name') takeActualLine
case branch' of
Just (name, branch) -> go branch name
Nothing -> putStrLn "Ok, nevermind." *> go branch name
["fork", newName0] -> do
let newName = pack newName0
branchExists <- Codebase.branchExists codebase newName
if branchExists
then do
putStrLn $ "Sorry, a branch by that name already exists."
go branch name
else do
branch' <- mergeBranchAndShowDiff codebase newName branch
go branch' newName
["merge", from] -> do
branch' <- Codebase.getBranch codebase $ pack from
case branch' of
Nothing -> do
putStrLn
$ "Sorry, I can't find a branch by that name to merge from."
go branch name
Just branch' -> do
branch'' <- mergeBranchAndShowDiff codebase name branch'
putStrLn $ "Flawless victory!"
go branch'' name
-- rename a term/type/... in the current branch
["rename", from, to]
-> let
terms = Branch.termsNamed (pack from) (Branch.head branch)
types = Branch.typesNamed (pack from) (Branch.head branch)
renameTerm branch = do
let branch' = Branch.modify (Branch.renameTerm (pack from) (pack to)) branch
mergeBranchAndShowDiff codebase name branch'
renameType branch = do
let branch' = Branch.modify (Branch.renameType (pack from) (pack to)) branch
mergeBranchAndShowDiff codebase name branch'
go' b = go b name
in
case (toList terms, toList types) of
([], []) -> putStrLn "I couldn't find anything by that name." >> go' branch
([_term], [] ) -> renameTerm branch >>= go'
([] , [_typ]) -> renameType branch >>= go'
([_term], [_typ]) -> do
putStrLn
"Do you want to rename the [term], [type], [both], or [neither]?"
putStr ">> "
(atomically . fmap words . takeLine) lineQueue >>= \case
["term"] -> renameTerm branch >>= go'
["type"] -> renameType branch >>= go'
["both"] -> renameTerm branch >>= renameType >>= go'
_ -> go' branch
(_terms, _types) -> do
-- idea: print out _terms and _types, so user can view them
putStrLn
$ "There's more than one thing called "
++ from
++ "."
putStrLn
$ "Use `> <command to resolve conflicts> unname "
++ from
++ "` to resolve conflicts, then try again."
go' branch
[] -> go branch name
x ->
putStrLn ("I don't know how to " ++ unwords x ++ ".")
*> go branch name
-- should never block
peekIncompleteLine :: TQueue Char -> STM String
peekIncompleteLine q = TQueue.tryPeekWhile (/= '\n') q
-- block until a full line is available
takeLine :: TQueue Char -> STM String
takeLine q = do
line <- TQueue.takeWhile (/= '\n') q
ch <- TQueue.dequeue q
if (ch /= '\n') then error "unpossibility in takeLine" else pure line
-- blocks until a line ending in '\n' is available
awaitCompleteLine :: TQueue Char -> STM ()
awaitCompleteLine ch = void $ TQueue.peekWhile (/= '\n') ch
-- let the user pick from a list of labeled `a`s
-- todo: rewrite this to let them toggle stuff
_multipleChoice :: [(String, a)] -> TQueue Char -> IO [a]
_multipleChoice as lineQueue = do
let render ((s, _), index) = putStrLn $ strPadLeft ' ' 5 ("[" ++ show index ++ "] ") ++ s
traverse_ render (as `zip` [(1::Int)..])
putStrLn "Please enter your selection as a space separated list of numbers."
putStr ">> "
numbers <- (atomically . fmap words . takeLine) lineQueue
case traverse Read.readMaybe numbers of
Nothing ->
putStrLn "Sorry, I couldn't understand at least one of those numbers."
>> _multipleChoice as lineQueue
Just numbers -> case find (\i -> i < 1 || i > length as) numbers of
Just i ->
(putStrLn $ "You entered the number " ++ show i ++ " which wasn't one of the choices.")
>> _multipleChoice as lineQueue
Nothing -> pure $ snd . (as !!) . (+ (-1)) <$> numbers
-- Merges `branch` into any the branch `name`, creating it if necessary.
mergeBranchAndShowDiff :: Monad m => Codebase m v a -> Name -> Branch -> m Branch
mergeBranchAndShowDiff codebase targetName sourceBranch = do
branch' <- Codebase.mergeBranch codebase targetName sourceBranch
-- when (branch' /= branch) $
-- putStrLn $ "Some extra stuff appeared right when you forked, "
-- ++ "and I went ahead and smashed it all together for you!"
pure branch'
foo :: Text -> (String, Text)
foo name = (unpack name, name)
selectBranch
:: Codebase IO v a -> Name -> IO String -> IO (Maybe (Name, Branch))
selectBranch codebase name takeLine = do
let branchMenu caption branches = Menu.menu1
takeLine -- console
caption -- caption
(fromString . unpack) -- render
(fromString . fmap Char.toLower . show) -- renderMeta
(foo <$> branches) -- groups
[("create", Create), ("cancel", Cancel)] -- metas
Nothing -- initial
branch <- Codebase.getBranch codebase name
case branch of
-- if branch named `name` exists, load it,
Just branch -> pure . Just $ (name, branch)
-- otherwise,
-- list branches that do exist, plus option to create, plus option to cancel
Nothing -> do
let caption =
fromString
$ "The branch "
++ show name
++ " doesn't exist. "
++ "Do you want to create it, or pick a different one?"
branches <- Codebase.branches codebase
choice <- branchMenu caption branches
case choice of
Just (Left Cancel) -> pure Nothing
Just (Left Create) -> do
branch <- mergeBranchAndShowDiff codebase name Codebase.builtinBranch
pure $ Just (name, branch)
Just (Right name) -> selectBranch codebase name takeLine
Nothing -> pure Nothing

View File

@ -51,7 +51,14 @@ import qualified Unison.Codebase.Editor.Actions as Actions
import Unison.Codebase.Runtime (Runtime)
import qualified Unison.Codebase.Runtime as Runtime
import qualified Unison.Codebase.Watch as Watch
import qualified Unison.HashQualified as HQ
import Unison.Name (Name)
import qualified Unison.Name as Name
import qualified Unison.Names as Names
import Unison.NamePrinter (prettyName,
prettyHashQualified,
styleHashQualified
)
import Unison.Parser (Ann)
import qualified Unison.PrettyPrintEnv as PPE
import Unison.PrintError (prettyParseError,
@ -86,11 +93,11 @@ notifyUser dir o = case o of
MissingThing r -> missing n r
BuiltinThing -> builtin n
RegularThing tm -> P.map fromString $
TermPrinter.prettyBinding ppe (Var.named n) tm
TermPrinter.prettyBinding ppe n tm
prettyTypes = map go2 types
builtin n = P.wrap $ "--" <> P.text n <> " is built-in."
builtin n = P.wrap $ "--" <> prettyHashQualified n <> " is built-in."
missing n r = P.wrap (
"-- The name " <> P.text n <> " is assigned to the "
"-- The name " <> prettyHashQualified n <> " is assigned to the "
<> "reference " <> fromString (show r ++ ",")
<> "which is missing from the codebase.")
<> P.newline
@ -159,7 +166,7 @@ notifyUser dir o = case o of
$ "I don't know of any "
<> fromString (Names.renderNameTarget nameTarget)
<> " named "
<> P.red (P.text name)
<> P.red (prettyName name)
<> " in the branch "
<> P.blue (P.text branchName)
<> "."
@ -170,7 +177,7 @@ notifyUser dir o = case o of
$ "There's already a "
<> fromString (Names.renderNameTarget nameTarget)
<> " named "
<> P.red (P.text name)
<> P.red (prettyName name)
<> " in the branch "
<> P.blue (P.text branchName)
<> "."
@ -179,7 +186,7 @@ notifyUser dir o = case o of
. warn
. P.wrap
$ "The name "
<> P.red (P.text name)
<> P.red (prettyName name)
<> " refers to more than one "
<> fromString (Names.renderNameTarget nameTarget)
<> " in the branch "
@ -203,7 +210,7 @@ notifyUser dir o = case o of
else " " <> P.text n
in intercalateMap "\n" go (sort branches)
ListOfDefinitions branch terms types ->
let ppe = Branch.prettyPrintEnv1 (Branch.head branch)
let ppe = Branch.prettyPrintEnv (Branch.head branch)
sigs0 = (\(name, _, typ) -> (name, typ)) <$> terms
sigs = [(name,t) | (name, Just t) <- sigs0 ]
impossible = terms >>= \case
@ -235,14 +242,14 @@ notifyUser dir o = case o of
termTypesFromFile =
Map.fromList [ (v,t) | (v,_,t) <- join (UF.topLevelComponents file) ]
ppe =
Branch.prettyPrintEnv1 (Branch.head branch) `PPE.unionLeft`
Branch.prettyPrintEnv1 (Branch.fromTypecheckedFile file)
Branch.prettyPrintEnv (Branch.head branch) `PPE.unionLeft`
Branch.prettyPrintEnv (Branch.fromTypecheckedFile file)
filterTermTypes vs =
[ (Var.name v,t) | v <- toList vs
[ (HQ.fromVar v,t) | v <- toList vs
, t <- maybe (error $ "There wasn't a type for " ++ show v ++ " in termTypesFromFile!") pure (Map.lookup v termTypesFromFile)]
prettyDeclHeader v = case UF.getDecl' file v of
Just (Left _) -> TypePrinter.prettyEffectHeader (Var.name v)
Just (Right _) -> TypePrinter.prettyDataHeader (Var.name v)
Just (Left _) -> TypePrinter.prettyEffectHeader (HQ.fromVar v)
Just (Right _) -> TypePrinter.prettyDataHeader (HQ.fromVar v)
Nothing -> error "Wat."
addMsg = if not (null addedTypes && null addedTerms)
then Just . P.okCallout $
@ -323,8 +330,8 @@ notifyUser dir o = case o of
Just (sampleName0, sampleExistingName0) =
(f . Branch.typeCollisions) (E.needsAlias s) <|>
(f . Branch.termCollisions) (E.needsAlias s)
sampleNewName' = P.group (P.text sampleName0 <> "`")
sampleOldName = P.text . head . toList $ sampleExistingName0 in
sampleNewName' = P.group (prettyName sampleName0 <> "`")
sampleOldName = prettyName . head . toList $ sampleExistingName0 in
P.wrap ("I skipped these definitions because they already" <> P.bold "exist with other names:") <> "\n\n" <>
P.indentN 2 (
@ -332,16 +339,17 @@ notifyUser dir o = case o of
P.align
-- ("type Optional", "aka " ++ commas existingNames)
-- todo: something is wrong here: only one oldName is being shown, instead of all
[(prettyDeclHeader $ Var.named newName,
"aka " <> P.commas (P.text <$> toList oldNames)) |
[(prettyDeclHeader $ Name.toVar newName,
"aka " <> P.commas (prettyName <$> toList oldNames)) |
(newName, oldNames) <-
Map.toList . R.domain . Branch.typeCollisions $ (E.needsAlias s) ],
TypePrinter.prettySignatures' ppe
TypePrinter.prettySignaturesAlt' ppe
-- foo, foo2, fasdf : a -> b -> c
[ (intercalateMap ", " id (name : toList oldNames), typ)
-- note: this shit vvvv is not a Name.
[ (name : fmap HQ.fromName (toList oldNames), typ)
| (newName, oldNames) <-
Map.toList . R.domain . Branch.termCollisions $ (E.needsAlias s)
, (name, typ) <- filterTermTypes [Var.named newName]
, (name, typ) <- filterTermTypes [Name.toVar newName]
]
])
<> "\n\n"
@ -354,17 +362,17 @@ notifyUser dir o = case o of
P.wrap ("I can't update these terms because the" <> P.bold "names are currently assigned to constructors:") <> "\n\n" <>
P.indentN 2
(P.column2
[ (P.text $ Var.name v
, "is a constructor for " <>
P.text (PPE.typeName ppe (Referent.toReference r)))
[ (P.text $ Var.name v, "is a constructor for " <> go r)
| (v, r) <- Map.toList termExistingCtorCollisions ]
)
<> "\n\n"
<> tip "You can `rename` these constructors to free up the names for your new definitions."
else Nothing
where
go r = prettyHashQualified (PPE.typeName ppe (Referent.toReference r))
ctorExistingTermCollisions = E.constructorExistingTermCollisions s
commaRefs rs = P.wrap $ P.commas (map go rs) where
go r = P.text (PPE.termName ppe r)
go r = prettyHashQualified (PPE.termName ppe r)
ctorExistingTermMsg =
if not (null ctorExistingTermCollisions)
then Just . P.warnCallout $
@ -409,10 +417,10 @@ notifyUser dir o = case o of
types = R.dom $ Branch.typeNamespace branch
when (not $ null terms) $ do
putStrLn "🙅 These terms have conflicts: "
traverse_ (\x -> putStrLn (" " ++ Text.unpack x)) terms
traverse_ (\x -> putStrLn (" " ++ Name.toString x)) terms
when (not $ null types) $ do
putStrLn "🙅 These types have conflicts: "
traverse_ (\x -> putStrLn (" " ++ Text.unpack x)) types
traverse_ (\x -> putStrLn (" " ++ Name.toString x)) types
-- TODO: Present conflicting TermEdits and TypeEdits
-- if we ever allow users to edit hashes directly.
FileChangeEvent _sourceName _src -> pure ()
@ -437,7 +445,7 @@ notifyUser dir o = case o of
<> "..."
]
TodoOutput branch todo ->
let ppe = Branch.prettyPrintEnv1 (Branch.head branch) in
let ppe = Branch.prettyPrintEnv (Branch.head branch) in
if E.todoScore todo == 0 && E.todoConflicts todo == mempty
then putPrettyLn . P.okCallout $ "No conflicts or edits in progress."
else do
@ -483,6 +491,7 @@ notifyUser dir o = case o of
])
where
renderNameConflicts :: Set.Set Name -> Set.Set Name -> [P.Pretty CT.ColorText]
renderNameConflicts conflictedTypeNames conflictedTermNames =
if null allNames then []
else [
@ -490,15 +499,15 @@ notifyUser dir o = case o of
if Set.null conflictedTypeNames then []
else [
P.wrap ("These" <> P.bold "types have conflicting definitions:")
`P.hang` P.commas (P.blue . P.text <$> toList conflictedTypeNames)
`P.hang` P.commas (P.blue . prettyName <$> toList conflictedTypeNames)
],
if Set.null conflictedTermNames then []
else [
P.wrap ("These" <> P.bold "terms have conflicting definitions:")
`P.hang` P.commas (P.blue . P.text <$> toList conflictedTermNames)
`P.hang` P.commas (P.blue . prettyName <$> toList conflictedTermNames)
],
[tip $ "This occurs when merging branches that both indepenently introduce the same name. Use "
<> P.group ("`view " <> P.sep " " (P.text <$> take 3 allNames) <> "`")
<> P.group ("`view " <> P.sep " " (prettyName <$> take 3 allNames) <> "`")
<> "to see the conflicting defintions, then use `rename`"
<> "and/or `replace` to resolve the conflicts."]
]
@ -516,21 +525,21 @@ notifyUser dir o = case o of
<> "to pick a replacement." -- todo: eventually something with `edit`
]]
where
name (Left (r,_)) = P.name P.bold (PPE.typeName ppe r)
name (Right (r,_)) = P.name P.bold (PPE.termName ppe (Referent.Ref r))
name (Left (r,_)) = styleHashQualified P.bold (PPE.typeName ppe r)
name (Right (r,_)) = styleHashQualified P.bold (PPE.termName ppe (Referent.Ref r))
formatTypeEdits es = P.wrap $ mconcat [
"was",
if TypeEdit.Deprecate `elem` es
then "deprecated and also replaced with"
else "replaced with",
P.oxfordCommas [ P.name P.bold (PPE.typeName ppe r) | TypeEdit.Replace r <- toList es ]
P.oxfordCommas [ styleHashQualified P.bold (PPE.typeName ppe r) | TypeEdit.Replace r <- toList es ]
]
formatTermEdits es = P.wrap $ mconcat [
"was",
if TermEdit.Deprecate `elem` es
then "deprecated and also replaced with"
else "replaced with",
P.oxfordCommas [ P.name P.bold (PPE.termName ppe (Referent.Ref r)) | TermEdit.Replace r _ <- toList es ]
P.oxfordCommas [ styleHashQualified P.bold (PPE.termName ppe (Referent.Ref r)) | TermEdit.Replace r _ <- toList es ]
]
formatConflict e@(Left (_, edits)) =
"The type " <> name e <> formatTypeEdits (toList edits)
@ -551,41 +560,41 @@ notifyUser dir o = case o of
when (not . Set.null $ E.changedSuccessfully r) . putPrettyLn . P.okCallout $
P.wrap $ "I" <> pastTenseCmd <> "the"
<> ns (E.changedSuccessfully r)
<> P.blue (P.text oldName)
<> "to" <> P.green (P.text (newName <> "."))
<> P.blue (prettyName oldName)
<> "to" <> P.group (P.green (prettyName newName) <> ".")
when (not . Set.null $ E.oldNameConflicted r) . putPrettyLn . P.warnCallout $
(P.wrap $ "I couldn't" <> cmd <> "the"
<> ns (E.oldNameConflicted r)
<> P.blue (P.text oldName)
<> "to" <> P.green (P.text newName)
<> P.blue (prettyName oldName)
<> "to" <> P.green (prettyName newName)
<> "because of conflicts.")
<> "\n\n"
<> tip "Use `todo` to view more information on conflicts and remaining work."
when (not . Set.null $ E.newNameAlreadyExists r) . putPrettyLn . P.warnCallout $
(P.wrap $ "I couldn't" <> cmd <> P.blue (P.text oldName)
<> "to" <> P.green (P.text newName)
(P.wrap $ "I couldn't" <> cmd <> P.blue (prettyName oldName)
<> "to" <> P.green (prettyName newName)
<> "because the "
<> ns (E.newNameAlreadyExists r)
<> "already exist(s).")
<> "\n\n"
<> tip
("Use" <> P.group ("`rename " <> P.text newName <> " <newname>`") <>
"to make" <> P.text newName <> "available.")
("Use" <> P.group ("`rename " <> prettyName newName <> " <newname>`") <>
"to make" <> prettyName newName <> "available.")
where
ns targets = P.oxfordCommas $
map (fromString . Names.renderNameTarget) (toList targets)
formatMissingStuff :: (Show a, Show b)
=> [(Names.Name, a)] -> [(Names.Name, b)] -> [P.Pretty P.ColorText]
formatMissingStuff :: (Show tm, Show typ)
=> [(HQ.HashQualified, tm)] -> [(HQ.HashQualified, typ)] -> [P.Pretty P.ColorText]
formatMissingStuff terms types = catMaybes [
(if null terms then Nothing else Just . P.fatalCallout $
P.wrap "The following terms have a missing or corrupted type signature:"
<> "\n\n"
<> P.column2 [ (P.text name, fromString (show ref)) | (name, ref) <- terms ]),
<> P.column2 [ (prettyHashQualified name, fromString (show ref)) | (name, ref) <- terms ]),
(if null types then Nothing else Just . P.fatalCallout $
P.wrap "The following types weren't found in the codebase:"
<> "\n\n"
<> P.column2 [ (P.text name, fromString (show ref)) | (name, ref) <- types ])
<> P.column2 [ (prettyHashQualified name, fromString (show ref)) | (name, ref) <- types ])
]
allow :: FilePath -> Bool
@ -692,7 +701,7 @@ validInputs = validPatterns
let bs = Text.unpack <$> branches
pure $ autoComplete q bs
definitionQueryArg = ArgumentType "definition query" $ \q _ b -> do
let names = Text.unpack <$> toList (Branch.allNames (Branch.head b))
let names = HQ.toString <$> toList (Branch.allNamesHashQualified (Branch.head b))
pure $ autoComplete q names
noCompletions = ArgumentType "a word" $ \_ _ _ -> pure []
quit = InputPattern
@ -809,8 +818,8 @@ validInputs = validPatterns
(\case
[oldName, newName] -> Right $ RenameUnconflictedI
allTargets
(Text.pack oldName)
(Text.pack newName)
(fromString oldName)
(fromString newName)
_ -> Left . P.warnCallout $ P.wrap
"`rename` takes two arguments, like `rename oldname newname`."
)
@ -822,8 +831,8 @@ validInputs = validPatterns
(\case
[oldName, newName] -> Right $ RenameUnconflictedI
allTargets
(Text.pack oldName)
(Text.pack newName)
(fromString oldName)
(fromString newName)
_ -> Left . P.warnCallout $ P.wrap
"`rename` takes two arguments, like `rename oldname newname`."
)
@ -837,8 +846,8 @@ validInputs = validPatterns
(\case
[oldName, newName] -> Right $ AliasUnconflictedI
allTargets
(Text.pack oldName)
(Text.pack newName)
(fromString oldName)
(fromString newName)
_ -> Left . warn $ P.wrap
"`alias` takes two arguments, like `alias oldname newname`."
)

View File

@ -34,8 +34,10 @@ import Unison.Codebase.Branch ( Branch
import qualified Unison.Codebase.Branch as Branch
import qualified Unison.DataDeclaration as DD
import Unison.FileParsers ( parseAndSynthesizeFile )
import Unison.Names ( Name
, Names
import Unison.HashQualified ( HashQualified )
import Unison.Name ( Name )
import qualified Unison.Name as Name
import Unison.Names ( Names
, NameTarget
)
import qualified Unison.Names as Names
@ -64,13 +66,12 @@ import qualified Unison.UnisonFile as UF
import Unison.Util.Free ( Free )
import qualified Unison.Util.Free as Free
import Unison.Var ( Var )
import qualified Unison.Var as Var
data Event
= UnisonFileChanged SourceName Text
| UnisonBranchChanged (Set Name)
| UnisonBranchChanged (Set BranchName)
type BranchName = Name
type BranchName = Text
type Source = Text -- "id x = x\nconst a b = a"
type SourceName = Text -- "foo.u" or "buffer 7"
type TypecheckingResult v =
@ -181,8 +182,8 @@ data Output v
| BranchAlreadyExists BranchName
| ListOfBranches BranchName [BranchName]
| ListOfDefinitions Branch
[(Name, Referent, Maybe (Type v Ann))]
[(Name, Reference, DisplayThing (Decl v Ann))]
[(HashQualified, Referent, Maybe (Type v Ann))]
[(HashQualified, Reference, DisplayThing (Decl v Ann))]
| SlurpOutput (SlurpResult v)
-- Original source, followed by the errors:
| ParseErrors Text [Parser.Err v]
@ -203,11 +204,11 @@ data TodoOutput v a
= TodoOutput_ {
todoScore :: Int,
todoFrontier ::
( [(Name, Reference, Maybe (Type v a))]
, [(Name, Reference, DisplayThing (Decl v a))]),
( [(HashQualified, Reference, Maybe (Type v a))]
, [(HashQualified, Reference, DisplayThing (Decl v a))]),
todoFrontierDependents ::
( [(Score, Name, Reference, Maybe (Type v a))]
, [(Score, Name, Reference, DisplayThing (Decl v a))]),
( [(Score, HashQualified, Reference, Maybe (Type v a))]
, [(Score, HashQualified, Reference, DisplayThing (Decl v a))]),
todoConflicts :: Branch0
} deriving (Show)
@ -305,12 +306,12 @@ data Command i v a where
-- Return a list of terms whose names match the given queries.
SearchTerms :: Branch
-> [String]
-> Command i v [(Name, Referent, Maybe (Type v Ann))]
-> Command i v [(HashQualified, Referent, Maybe (Type v Ann))]
-- Return a list of types whose names match the given queries.
SearchTypes :: Branch
-> [String]
-> Command i v [(Name, Reference)] -- todo: can add Kind later
-> Command i v [(HashQualified, Reference)] -- todo: can add Kind later
LoadTerm :: Reference.Id -> Command i v (Maybe (Term v Ann))
@ -397,11 +398,11 @@ outcomes okToUpdate b file = let
_otherwise -> (r0, CouldntUpdateConflicted) -- come back to this
outcomes0terms = map termOutcome (Map.toList $ UF.hashTerms file)
termOutcome (v, (r, _, _)) = outcome0 (Var.name v) (Right r) []
termOutcome (v, (r, _, _)) = outcome0 (Name.unsafeFromVar v) (Right r) []
outcomes0types
= map typeOutcome (Map.toList . fmap (second Right) $ UF.dataDeclarations' file)
++ map typeOutcome (Map.toList . fmap (second Left) $ UF.effectDeclarations' file)
typeOutcome (v, (r, dd)) = outcome0 (Var.name v) (Left r) $ ctorNames v r dd
typeOutcome (v, (r, dd)) = outcome0 (Name.unsafeFromVar v) (Left r) $ ctorNames v r dd
ctorNames v r (Left e) = Map.keys $ Names.termNames (DD.effectDeclToNames v r e)
ctorNames v r (Right dd) = Map.keys $ Names.termNames (DD.dataDeclToNames v r dd)
outcomes0 = outcomes0terms ++ outcomes0types
@ -487,14 +488,15 @@ fileToBranch handleCollisions codebase branch uf = do
, Branch.fromDeclaration v r dd <> b )
Right r ->
( result { adds = adds result <> SlurpComponent mempty (Set.singleton v) }
, Branch.addTermName (Referent.Ref r) (Var.name v) b )
, Branch.addTermName (Referent.Ref r) (Name.unsafeFromVar v) b )
Updated -> do
let result' = result { updates = updates result <> sc r v }
name = Name.unsafeFromVar v
case r of
Left (r', dd) -> case toList (Branch.typesNamed (Var.name v) b0) of
Left (r', dd) -> case toList (Branch.typesNamed name b0) of
[r0] -> pure (result', Branch.fromDeclaration v r' dd <> Branch.replaceType r0 r' b)
_ -> error "Panic. Tried to replace a type that's conflicted."
Right r' -> case toList (Branch.termsNamed (Var.name v) b0) of
Right r' -> case toList (Branch.termsNamed name b0) of
[Referent.Ref r0] -> do
Just type1 <- Codebase.getTypeOfTerm codebase r0
let Just (_, _, type2) = Map.lookup r' termsByRef
@ -502,7 +504,7 @@ fileToBranch handleCollisions codebase branch uf = do
if Typechecker.isEqual type1 type2 then TermEdit.Same
else if Typechecker.isSubtype type2 type1 then TermEdit.Subtype
else TermEdit.Different
pure (result', Branch.addTermName (Referent.Ref r') (Var.name v) $
pure (result', Branch.addTermName (Referent.Ref r') name $
Branch.replaceTerm r0 r' typing b)
_ -> error $ "Panic. Tried to replace a term that's conflicted." ++ show v
AlreadyExists -> pure (result { duplicates = duplicates result <> sc r v }, b)
@ -510,15 +512,16 @@ fileToBranch handleCollisions codebase branch uf = do
CouldntUpdateConflicted ->
pure (result { conflicts = conflicts result <> sc r v }, b)
RequiresAlias ns -> let
name = Name.unsafeFromVar v
rcs = case r of
Left _ -> Branch.RefCollisions mempty (R.fromList $ (Var.name v,) <$> ns)
Right _ -> Branch.RefCollisions (R.fromList $ (Var.name v,) <$> ns) mempty
Left _ -> Branch.RefCollisions mempty (R.fromList $ (name,) <$> ns)
Right _ -> Branch.RefCollisions (R.fromList $ (name,) <$> ns) mempty
in pure (result { needsAlias = needsAlias result <> rcs }, b)
TermExistingConstructorCollision ->
pure (result {
termExistingConstructorCollisions =
termExistingConstructorCollisions result <>
pick (toList $ Branch.constructorsNamed (Var.name v) b0) }, b)
pick (toList $ Branch.constructorsNamed (Name.unsafeFromVar v) b0) }, b)
where
pick [] = error "Panic. Incorrectly determined a conflict."
pick (h:_) = Map.fromList [(v, h)]
@ -649,7 +652,7 @@ doTodo code b = do
f <- Codebase.frontier code b
let dirty = R.dom f
frontier = R.ran f
ppe = Branch.prettyPrintEnv1 b
ppe = Branch.prettyPrintEnv b
(frontierTerms, frontierTypes) <- loadDefinitions code frontier
(dirtyTerms, dirtyTypes) <- loadDefinitions code dirty
-- todo: something more intelligent here?
@ -688,4 +691,3 @@ loadDefinitions code refs = do
Just d -> pure (r, RegularThing d)
_ -> error $ "unpossible " ++ show r
pure (terms, types)

View File

@ -49,9 +49,8 @@ import Unison.Codebase.Editor ( Command(..)
, collateReferences
)
import qualified Unison.Codebase.Editor as Editor
import Unison.Names ( Name
, NameTarget
)
import Unison.Name ( Name )
import Unison.Names ( NameTarget )
import qualified Unison.Names as Names
import Unison.Parser ( Ann )
import qualified Unison.PrettyPrintEnv as PPE
@ -181,7 +180,7 @@ loop s = Free.unfold' (evalStateT (maybe (Left ()) Right <$> runMaybeT (go *> ge
PPE.fromTermNames [ (r, n) | (n, r, _) <- terms ]
`PPE.unionLeft` PPE.fromTypeNames (swap <$> types)
`PPE.unionLeft` Branch.prettyPrintEnv
[Branch.head $ currentBranch']
(Branch.head currentBranch')
loc = case outputLoc of
Editor.ConsoleLocation -> Nothing
Editor.FileLocation path -> Just path
@ -411,4 +410,3 @@ updateBranch
:: Action i v () -> BranchName -> (Branch -> Branch) -> Action i v ()
updateBranch success branchName f =
withBranch branchName $ \b -> merging branchName (f b) success

View File

@ -2,6 +2,7 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
module Unison.Codebase.FileCodebase where
@ -50,10 +51,12 @@ import Text.Read ( readMaybe )
import qualified Unison.Builtin as Builtin
import Unison.Codebase ( Codebase(Codebase)
, Err(InvalidBranchFile)
, BranchName
)
import Unison.Codebase.Branch ( Branch )
import qualified Unison.Codebase.Branch as Branch
import Unison.Names ( Name )
import qualified Unison.Name as Name
import Unison.Name ( Name )
import qualified Unison.Codebase.Serialization as S
import qualified Unison.Codebase.Serialization.V0
as V0
@ -130,17 +133,19 @@ termDir, declDir:: FilePath -> Reference.Id -> FilePath
termDir path r = path </> "terms" </> componentId r
declDir path r = path </> "types" </> componentId r
encodeName :: Name -> FilePath
encodeName = Hash.base58s . Hash.fromBytes . encodeUtf8
encodeBuiltinName :: Name -> FilePath
encodeBuiltinName = Hash.base58s . Hash.fromBytes . encodeUtf8 . Name.toText
decodeName :: FilePath -> Maybe Name
decodeName p = decodeUtf8 . Hash.toBytes <$> Hash.fromBase58 (Text.pack p)
decodeBuiltinName :: FilePath -> Maybe Name
decodeBuiltinName p =
Name.unsafeFromText . decodeUtf8 . Hash.toBytes <$>
Hash.fromBase58 (Text.pack p)
builtinTermDir, builtinTypeDir :: FilePath -> Name -> FilePath
builtinTermDir path name =
path </> "terms" </> "_builtin" </> encodeName name
path </> "terms" </> "_builtin" </> encodeBuiltinName name
builtinTypeDir path name =
path </> "types" </> "_builtin" </> encodeName name
path </> "types" </> "_builtin" </> encodeBuiltinName name
termPath, typePath, declPath :: FilePath -> Reference.Id -> FilePath
termPath path r = termDir path r </> "compiled.ub"
@ -220,7 +225,7 @@ codebase1 builtinTypeAnnotation (S.Format getV putV) (S.Format getA putA) path
-- delete any leftover branch files "before" this one,
-- and write this one if it doesn't already exist.
overwriteBranch :: Name -> Branch -> IO ()
overwriteBranch :: BranchName -> Branch -> IO ()
overwriteBranch name branch = do
let newBranchHash = Hash.base58s . Branch.toHash $ branch
(match, nonmatch) <-
@ -256,7 +261,7 @@ codebase1 builtinTypeAnnotation (S.Format getV putV) (S.Format getA putA) path
else pure Set.empty
where
dir = case r of
Reference.Builtin name ->
Reference.Builtin (Name.unsafeFromText -> name) ->
pure $ (if Builtin.isBuiltinTerm name
then builtinTermDir
else builtinTypeDir
@ -268,7 +273,7 @@ codebase1 builtinTypeAnnotation (S.Format getV putV) (S.Format getA putA) path
pure $ (if b then termDir else declDir) path id
_ -> error "impossible: these patterns should be enough"
branchUpdates :: IO (IO (), IO (Set Name))
branchUpdates :: IO (IO (), IO (Set BranchName))
branchUpdates = do
branchFileChanges <- TQueue.newIO
(cancelWatch, watcher) <- Watch.watchDirectory' (branchesPath path)
@ -300,5 +305,5 @@ codebase1 builtinTypeAnnotation (S.Format getV putV) (S.Format getA putA) path
dependents
builtinTypeAnnotation
ubfPathToName :: FilePath -> Name
ubfPathToName :: FilePath -> BranchName
ubfPathToName = Text.pack . takeFileName . takeDirectory

View File

@ -52,6 +52,8 @@ import qualified Unison.Codebase.TypeEdit as TypeEdit
import qualified Unison.Codebase.Serialization as S
import qualified Unison.Hash as Hash
import qualified Unison.Kind as Kind
import Unison.Name (Name)
import qualified Unison.Name as Name
import qualified Unison.Reference as Reference
import Unison.Referent (Referent)
import qualified Unison.Referent as Referent
@ -487,18 +489,24 @@ getTypeEdit = getWord8 >>= \case
putBranch :: MonadPut m => Branch -> m ()
putBranch (Branch b) = putCausal b $ \b -> do
putRelation (Branch.termNamespace b) putText putReferent
putRelation (Branch.typeNamespace b) putText putReference
putRelation (Branch.oldTermNamespace b) putText putReferent
putRelation (Branch.oldTypeNamespace b) putText putReference
putRelation (Branch.termNamespace b) putName putReferent
putRelation (Branch.typeNamespace b) putName putReference
putRelation (Branch.oldTermNamespace b) putName putReferent
putRelation (Branch.oldTypeNamespace b) putName putReference
putRelation (Branch.editedTerms b) putReference putTermEdit
putRelation (Branch.editedTypes b) putReference putTypeEdit
putName :: MonadPut m => Name -> m ()
putName = putText . Name.toText
getName :: MonadGet m => m Name
getName = Name.unsafeFromText <$> getText
getNamespace :: MonadGet m => m Branch.Namespace
getNamespace =
Branch.Namespace
<$> getRelation getText getReferent
<*> getRelation getText getReference
<$> getRelation getName getReferent
<*> getRelation getName getReference
getBranch :: MonadGet m => m Branch
getBranch = Branch <$> getCausal

View File

@ -19,6 +19,7 @@ import Prelude.Extras (Show1)
import qualified Unison.ABT as ABT
import Unison.Hashable (Accumulate, Hashable1)
import qualified Unison.Hashable as Hashable
import qualified Unison.Name as Name
import Unison.Reference (Reference)
import qualified Unison.Reference as Reference
import Unison.Referent (Referent)
@ -114,7 +115,7 @@ toNames0
-> Names
toNames0 typeSymbol r f dd =
let names (ctor, i) =
let name = Var.qualifiedName ctor in Names.fromTerms [(name, f r i)]
let name = Name.unsafeFromVar ctor in Names.fromTerms [(name, f r i)]
in foldMap names (constructorVars dd `zip` [0 ..])
<> Names.fromTypesV [(typeSymbol, r)]
@ -258,4 +259,3 @@ bindDecls decls refs = sortCtors . bindBuiltins refs <$> decls
sortCtors dd =
DataDeclaration (annotation dd) (bound dd) (sortOn hash3 $ constructors' dd)
hash3 (_, _, typ) = ABT.hash typ :: Hash

View File

@ -23,7 +23,8 @@ import qualified Unison.Blank as Blank
import qualified Unison.Codecs as Codecs
import Unison.DataDeclaration (DataDeclaration',
EffectDeclaration')
import Unison.Names (Name, Names)
import qualified Unison.Name as Name
import Unison.Names (Names)
import qualified Unison.Names as Names
import Unison.Parser (Ann (Intrinsic))
import qualified Unison.Parsers as Parsers
@ -50,6 +51,7 @@ type EffectDeclaration v = EffectDeclaration' v Ann
type UnisonFile v = UF.UnisonFile v Ann
type NamedReference v = Typechecker.NamedReference v Ann
type Result' v = Result (Seq (Note v Ann))
type Name = Text
-- move to Unison.Util.List
-- prefers earlier copies
@ -117,9 +119,10 @@ synthesizeFile preexistingTypes preexistingNames unisonFile = do
where
fqnsByShortName :: Map Name [Typechecker.NamedReference v Ann]
fqnsByShortName = Map.fromListWith mappend
[ (Names.unqualified name,
[ (Names.unqualified' name,
[Typechecker.NamedReference name typ (Right r)]) |
(name, r) <- Map.toList $ Names.termNames allTheNames,
(name', r) <- Map.toList $ Names.termNames allTheNames,
let name = Name.toText name',
typ <- Foldable.toList $ TL.typeOfReferent lookupTypes r ]
Result notes mayType =
evalStateT (Typechecker.synthesizeAndResolve env0) tdnrTerm

View File

@ -0,0 +1,81 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Unison.HashQualified where
import Data.String (IsString, fromString)
import Data.Text (Text)
import qualified Data.Text as Text
import Unison.Name (Name)
import qualified Unison.Name as Name
import Unison.Reference (Reference)
import qualified Unison.Reference as Reference
import Unison.Referent (Referent)
import qualified Unison.Referent as Referent
import Unison.Var (Var)
import qualified Unison.Var as Var
data HashQualified
-- todo: Let HashOnly take Reference instead of Text?
-- This requires being able to parse a Reference in
= NameOnly Name | HashOnly Referent | HashQualified Name Text
deriving (Eq, Ord)
toName :: HashQualified -> Maybe Name
toName = \case
NameOnly name -> Just name
HashQualified name _ -> Just name
HashOnly _ -> Nothing
hashSeparator :: Text
hashSeparator = "#"
toString :: HashQualified -> String
toString = Text.unpack . toText
fromString :: String -> HashQualified
fromString = fromText . Text.pack
-- parses possibly-hash-qualified into structured type
fromText :: Text -> HashQualified
fromText t =
case Text.breakOn hashSeparator t of
("", "") -> error "don't give me that"
(name, "") -> NameOnly (Name.unsafeFromText name)
("", hash) -> HashOnly (Referent.unsafeFromText hash)
(name, hash) -> HashQualified (Name.unsafeFromText name) hash
toText :: HashQualified -> Text
toText = \case
NameOnly name -> Name.toText name
HashQualified name hash -> Name.toText name <> hash
HashOnly ref -> Text.pack (show ref)
forReferent :: Referent -> Int -> Name -> HashQualified
forReferent r len n =
HashQualified n . Text.pack $ Referent.showShort len r
forReference :: Reference -> Int -> Name -> HashQualified
forReference r len n =
HashQualified n . Text.pack $ Reference.showShort len r
fromReferent :: Referent -> HashQualified
fromReferent = HashOnly
fromReference :: Reference -> HashQualified
fromReference = HashOnly . Referent.Ref
fromName :: Name -> HashQualified
fromName n = NameOnly n
fromVar :: Var v => v -> HashQualified
fromVar = fromText . Var.name
toVar :: Var v => HashQualified -> v
toVar = Var.named . toText
instance IsString HashQualified where
fromString = fromText . Text.pack
instance Show HashQualified where
show = Text.unpack . toText

View File

@ -0,0 +1,33 @@
module Unison.Name (Name(..), unsafeFromText, toString, fromString, toVar, unsafeFromVar) where
import Data.String (IsString, fromString)
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Unison.Hashable as H
import Unison.Var (Var)
import qualified Unison.Var as Var
newtype Name = Name { toText :: Text } deriving (Eq, Ord)
unsafeFromText :: Text -> Name
unsafeFromText t =
if Text.any (=='#') t then error $ "not a name: " <> show t
else Name t
toVar :: Var v => Name -> v
toVar (Name t) = Var.named t
unsafeFromVar :: Var v => v -> Name
unsafeFromVar = unsafeFromText . Var.name
toString :: Name -> String
toString = Text.unpack . toText
instance Show Name where
show = toString
instance IsString Name where
fromString = unsafeFromText . Text.pack
instance H.Hashable Name where
tokens s = [H.Text (toText s)]

View File

@ -0,0 +1,25 @@
{-# LANGUAGE LambdaCase #-}
module Unison.NamePrinter where
import Data.String (IsString, fromString)
import Unison.HashQualified (HashQualified)
import qualified Unison.HashQualified as HQ
import Unison.Name (Name)
import qualified Unison.Name as Name
import qualified Unison.Referent as Referent
import Unison.Util.Pretty (Pretty)
import qualified Unison.Util.Pretty as PP
prettyName :: IsString s => Name -> Pretty s
prettyName = PP.text . Name.toText
prettyHashQualified :: IsString s => HashQualified -> Pretty s
prettyHashQualified = PP.text . HQ.toText
styleHashQualified ::
IsString s => (Pretty s -> Pretty s) -> HashQualified -> Pretty s
styleHashQualified style = \case
HQ.NameOnly n -> style (prettyName n)
HQ.HashOnly r -> fromString . Referent.toString $ r
HQ.HashQualified n h -> PP.group $ style (prettyName n) <> PP.text h

View File

@ -10,9 +10,12 @@ import Data.Bifunctor (first)
import Data.List (foldl')
import Data.Map (Map)
import qualified Data.Map as Map
import Data.String (fromString)
import Data.Text (Text)
import qualified Data.Text as Text
import Unison.Reference (pattern Builtin, Reference)
import qualified Unison.Name as Name
import Unison.Name (Name)
import qualified Unison.Referent as Referent
import Unison.Referent (Referent)
import Unison.Term (AnnotatedTerm)
@ -20,12 +23,12 @@ import qualified Unison.Term as Term
import Unison.Type (AnnotatedType)
import qualified Unison.Type as Type
import Unison.Var (Var)
import qualified Unison.Var as Var
type Name = Text
unqualified :: Name -> Name
unqualified = last . Text.splitOn "."
unqualified = Name.unsafeFromText . unqualified' . Name.toText
unqualified' :: Text -> Text
unqualified' = last . Text.splitOn "."
data Names = Names
{ termNames :: Map Name Referent
@ -54,14 +57,15 @@ lookupType ns n = Map.lookup n (typeNames ns)
fromBuiltins :: [Reference] -> Names
fromBuiltins rs =
mempty { termNames = Map.fromList [ (name, Referent.Ref r) | r@(Builtin name) <- rs ] }
mempty { termNames = Map.fromList
[ (Name.unsafeFromText t, Referent.Ref r) | r@(Builtin t) <- rs ] }
fromTerms :: [(Name, Referent)] -> Names
fromTerms ts = mempty { termNames = Map.fromList ts }
fromTypesV :: Var v => [(v, Reference)] -> Names
fromTypesV env =
Names mempty . Map.fromList $ fmap (first $ Var.name) env
Names mempty . Map.fromList $ fmap (first $ Name.unsafeFromVar) env
fromTypes :: [(Name, Reference)] -> Names
fromTypes env = Names mempty $ Map.fromList env
@ -72,7 +76,7 @@ filterTypes f (Names {..}) = Names termNames m2
m2 = Map.fromList $ [(k,v) | (k,v) <- Map.toList typeNames, f k]
patternNameds :: Names -> String -> Maybe (Reference, Int)
patternNameds ns s = patternNamed ns (Text.pack s)
patternNameds ns s = patternNamed ns (fromString s)
patternNamed :: Names -> Name -> Maybe (Reference, Int)
patternNamed ns n = Map.lookup n (termNames ns) >>= \case
@ -83,16 +87,16 @@ patternNamed ns n = Map.lookup n (termNames ns) >>= \case
bindType :: Var v => Names -> AnnotatedType v a -> AnnotatedType v a
bindType ns t = Type.bindBuiltins typeNames' t
where
typeNames' = [ (Var.named v, r) | (v, r) <- Map.toList $ typeNames ns ]
typeNames' = [ (Name.toVar v, r) | (v, r) <- Map.toList $ typeNames ns ]
bindTerm
:: forall v a . Var v => Names -> AnnotatedTerm v a -> AnnotatedTerm v a
bindTerm ns e = Term.bindBuiltins termBuiltins typeBuiltins e
where
termBuiltins =
[ (Var.named v, Term.fromReferent() e) | (v, e) <- Map.toList (termNames ns) ]
[ (Name.toVar v, Term.fromReferent() e) | (v, e) <- Map.toList (termNames ns) ]
typeBuiltins :: [(v, Reference)]
typeBuiltins = [ (Var.named v, t) | (v, t) <- Map.toList (typeNames ns) ]
typeBuiltins = [ (Name.toVar v, t) | (v, t) <- Map.toList (typeNames ns) ]
-- Given a mapping from name to qualified name, update a `PEnv`,
-- so for instance if the input has [(Some, Optional.Some)],
@ -105,7 +109,7 @@ importing shortToLongName0 (Names {..}) = let
Nothing -> m
Just v -> Map.insert shortname v m
shortToLongName = [
(Var.name v, Var.name v2) | (v,v2) <- shortToLongName0 ]
(Name.unsafeFromVar v, Name.unsafeFromVar v2) | (v,v2) <- shortToLongName0 ]
terms' = foldl' go termNames shortToLongName
types' = foldl' go typeNames shortToLongName
in Names terms' types'

View File

@ -2,24 +2,22 @@
module Unison.PrettyPrintEnv where
import Data.List (foldl')
import Data.Map (Map)
import Control.Applicative ((<|>))
import Data.Maybe (fromMaybe)
import Unison.Reference (Reference)
import qualified Data.Map as Map
import qualified Data.Text as Text
import Unison.Names (Name,Names)
import Unison.HashQualified (HashQualified)
import qualified Unison.HashQualified as HQ
import Unison.Names (Names)
import qualified Unison.Names as Names
import Unison.Referent (Referent)
import qualified Unison.Referent as Referent
type Histogram = Map Name Word
-- Maps terms, types, constructors and constructor patterns to a histogram of names.
data PrettyPrintEnv = PrettyPrintEnv {
-- names for terms, constructors, and requests
terms :: Referent -> Histogram,
terms :: Referent -> Maybe HashQualified,
-- names for types
types :: Reference -> Histogram }
types :: Reference -> Maybe HashQualified }
instance Show PrettyPrintEnv where
show _ = "PrettyPrintEnv"
@ -27,114 +25,39 @@ instance Show PrettyPrintEnv where
fromNames :: Names -> PrettyPrintEnv
fromNames ns =
let terms =
Map.fromList [ (r, n) | (n, r) <- Map.toList (Names.termNames ns) ]
Map.fromList [ (r, HQ.fromName n) | (n, r) <- Map.toList (Names.termNames ns) ]
types =
Map.fromList [ (r, n) | (n, r) <- Map.toList (Names.typeNames ns) ]
hist :: Ord k => Map k Name -> k -> Histogram
hist m k = maybe mempty (\n -> Map.fromList [(n, 1)]) $ Map.lookup k m
in PrettyPrintEnv (hist terms) (hist types)
-- The monoid sums corresponding histograms
instance Semigroup PrettyPrintEnv where (<>) = mappend
instance Monoid PrettyPrintEnv where
mempty = PrettyPrintEnv (const mempty) (const mempty)
mappend e1 e2 =
PrettyPrintEnv
(\r -> Map.unionWith (+) (terms e1 r) (terms e2 r))
(\r -> Map.unionWith (+) (types e1 r) (types e2 r))
Map.fromList [ (r, HQ.fromName n) | (n, r) <- Map.toList (Names.typeNames ns) ]
in PrettyPrintEnv (`Map.lookup` terms) (`Map.lookup` types)
-- Left-biased union of environments
unionLeft :: PrettyPrintEnv -> PrettyPrintEnv -> PrettyPrintEnv
unionLeft e1 e2 = PrettyPrintEnv
(\r -> prefer (terms e1 r) (terms e2 r))
(\r -> prefer (types e1 r) (types e2 r))
where prefer h1 h2 = if sum (Map.elems h1) > 0 then h1 else h2
(\r -> terms e1 r <|> terms e2 r)
(\r -> types e1 r <|> types e2 r)
adjust :: (Word -> Word) -> PrettyPrintEnv -> PrettyPrintEnv
adjust by e = PrettyPrintEnv
(\r -> by <$> terms e r)
(\r -> by <$> types e r)
assignTermName :: Referent -> HashQualified -> PrettyPrintEnv -> PrettyPrintEnv
assignTermName r name = (fromTermNames [(r,name)] `unionLeft`)
scale :: Word -> PrettyPrintEnv -> PrettyPrintEnv
scale by = adjust (by *)
incrementBy :: Word -> PrettyPrintEnv -> PrettyPrintEnv
incrementBy by = adjust (by +)
weightedSum :: [(Word,PrettyPrintEnv)] -> PrettyPrintEnv
weightedSum envs = mconcat (uncurry scale <$> envs)
fromTypeNames :: [(Reference,Name)] -> PrettyPrintEnv
fromTypeNames :: [(Reference,HashQualified)] -> PrettyPrintEnv
fromTypeNames types = let
m = Map.fromList types
toH Nothing = mempty
toH (Just t) = Map.fromList [(t, 1)]
in mempty { types = \r -> toH $ Map.lookup r m }
in PrettyPrintEnv (const Nothing) (`Map.lookup` m)
fromTermNames :: [(Referent,Name)] -> PrettyPrintEnv
fromTermNames :: [(Referent,HashQualified)] -> PrettyPrintEnv
fromTermNames tms = let
m = Map.fromList tms
toH Nothing = mempty
toH (Just t) = Map.fromList [(t, 1)]
in mempty { terms = \r -> toH $ Map.lookup r m }
in PrettyPrintEnv (`Map.lookup` m) (const Nothing)
fromConstructorNames :: [((Reference,Int), Name)] -> [((Reference,Int), Name)] -> PrettyPrintEnv
fromConstructorNames ctors reqs = let
cs = Map.fromList ctors
rs = Map.fromList reqs
toH Nothing = mempty
toH (Just t) = Map.fromList [(t, 1)]
in mempty { terms = \r -> case r of
Referent.Con r i -> toH $ Map.lookup (r,i) cs
Referent.Req r i -> toH $ Map.lookup (r,i) rs
_ -> mempty }
termName :: PrettyPrintEnv -> Referent -> HashQualified
termName env r = fromMaybe (HQ.fromReferent r) (terms env r)
-- These functions pick out the most common name and fall back
-- to showing the `Reference` if no names are available
typeName :: PrettyPrintEnv -> Reference -> HashQualified
typeName env r = fromMaybe (HQ.fromReferent (Referent.Ref r)) (types env r)
termName :: PrettyPrintEnv -> Referent -> Name
termName env r = pickNameReferent r (terms env r)
typeName :: PrettyPrintEnv -> Reference -> Name
typeName env r = pickName r (types env r)
constructorName :: PrettyPrintEnv -> Reference -> Int -> Name
constructorName env r cid = pickNameCid r cid (terms env (Referent.Con r cid))
requestName :: PrettyPrintEnv -> Reference -> Int -> Name
requestName env r cid = pickNameCid r cid (terms env (Referent.Req r cid))
patternName :: PrettyPrintEnv -> Reference -> Int -> Name
patternName env r cid = pickNameCid r cid histo
where
histo = Map.unionWith (+)
(terms env (Referent.Con r cid))
(terms env (Referent.Req r cid))
pickName :: Reference -> Histogram -> Name
pickName r h = case argmax snd (Map.toList h) of
Nothing -> Text.pack (show r)
Just (name,_) -> name
pickNameReferent :: Referent -> Histogram -> Name
pickNameReferent r h = case argmax snd (Map.toList h) of
Nothing -> case r of
Referent.Ref r -> Text.pack (show r)
Referent.Con r i -> Text.pack (show r <> "#" <> show i)
Referent.Req r i -> Text.pack (show r <> "#" <> show i)
Just (name,_) -> name
pickNameCid :: Reference -> Int -> Histogram -> Name
pickNameCid r cid h = case argmax snd (Map.toList h) of
Nothing -> Text.pack (show r) <> "#" <> Text.pack (show cid)
Just (name,_) -> name
-- this fn should really exist someplace else
argmax :: (Foldable f, Ord b) => (a -> b) -> f a -> Maybe a
argmax by as = fst <$> foldl' go Nothing as where
go Nothing a = Just (a, by a)
go cur@(Just (_,b)) a2 =
let b2 = by a2
in if b2 > b then Just (a2,b2) else cur
patternName :: PrettyPrintEnv -> Reference -> Int -> HashQualified
patternName env r cid = fromMaybe (HQ.fromReferent (Referent.Con r cid)) $
terms env (Referent.Con r cid) <|> terms env (Referent.Req r cid)
-- arbitrarily pick Con because it is only used to determine how to render
-- the referent as text. We haven't chosen any rendering distinction between
-- Req and Con.

View File

@ -32,6 +32,7 @@ import Data.Void (Void)
import qualified Text.Megaparsec as P
import qualified Unison.ABT as ABT
import qualified Unison.DataDeclaration as DD
import qualified Unison.HashQualified as HQ
import Unison.Kind (Kind)
import qualified Unison.Kind as Kind
import qualified Unison.Lexer as L
@ -702,7 +703,7 @@ renderTerm :: (IsString s, Var v) => Env -> C.Term v loc -> s
renderTerm _ (ABT.Var' v) | Settings.demoHideVarNumber =
fromString (Text.unpack $ Var.name v)
renderTerm env (Term.Ref' r) =
fromString (Text.unpack $ PPE.termName env (Referent.Ref r))
fromString (HQ.toString $ PPE.termName env (Referent.Ref r))
renderTerm _ e =
let s = show e
in -- todo: pretty print
@ -783,14 +784,14 @@ renderKind Kind.Star = "*"
renderKind (Kind.Arrow k1 k2) = renderKind k1 <> " -> " <> renderKind k2
showTermRef :: IsString s => Env -> Referent -> s
showTermRef env r = fromString . Text.unpack $ PPE.termName env r
showTermRef env r = fromString . HQ.toString $ PPE.termName env r
showTypeRef :: IsString s => Env -> R.Reference -> s
showTypeRef env r = fromString . Text.unpack $ PPE.typeName env r
showTypeRef env r = fromString . HQ.toString $ PPE.typeName env r
-- todo: do something different/better if cid not found
showConstructor :: IsString s => Env -> R.Reference -> Int -> s
showConstructor env r cid = fromString . Text.unpack $
showConstructor env r cid = fromString . HQ.toString $
PPE.patternName env r cid
styleInOverallType

View File

@ -1,5 +1,7 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
module Unison.Reference
(Reference(DerivedPrivate_),
@ -13,23 +15,24 @@ module Unison.Reference
hashComponents,
groupByComponent,
componentFor,
unsafeFromText,
showShort) where
import GHC.Generics
import Data.Maybe (fromJust)
import Unison.Hashable as Hashable
import qualified Data.Text as Text
import qualified Unison.Hash as H
import Data.Word (Word64)
import Control.Monad (join)
import qualified Data.Map as Map
import qualified Data.Set as Set
import Data.Set (Set)
import Data.List
import Data.Foldable (toList)
import Data.Text (Text)
import qualified Unison.ABT as ABT
import qualified Unison.Var as Var
import Control.Monad (join)
import Data.Foldable (toList)
import Data.List
import qualified Data.Map as Map
import Data.Maybe (fromJust)
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Word (Word64)
import GHC.Generics
import qualified Unison.ABT as ABT
import qualified Unison.Hash as H
import Unison.Hashable as Hashable
import qualified Unison.Var as Var
data Reference
= Builtin_ Text.Text
@ -43,7 +46,7 @@ data Id = Id H.Hash Pos Size deriving (Eq,Ord,Generic)
instance Show Id where
show (Id h 0 1) = show h
show (Id h i _) = show h <> "-" <> show i
show (Id h i n) = show h <> "-" <> show i <> "-" <> show n
pattern Builtin t = Builtin_ t
pattern Derived h n i = DerivedPrivate_ (Id h n i)
@ -67,6 +70,20 @@ derivedBase58 b58 i n = DerivedPrivate_ (Id (fromJust h) i n)
where
h = H.fromBase58 b58
-- Parses Asdf##Foo as Builtin Foo
-- Parses Asdf#abc123-1-2 as Derived 'abc123' 1 2
unsafeFromText :: Text -> Reference
unsafeFromText t = case Text.split (=='#') t of
[_, "", b] -> Builtin b
[_, h] -> case Text.split (=='-') h of
[hash] -> derivedBase58 hash 0 1
[hash, pos, size] -> derivedBase58 hash (read . Text.unpack $ pos)
(read . Text.unpack $ size)
_ -> bail
_ -> bail
where bail = error . Text.unpack $ "couldn't parse a Reference from " <> t
hashComponents ::
(Functor f, Hashable1 f, Foldable f, Eq v, Var.Var v)
=> (Reference -> ABT.Term f v ())
@ -99,8 +116,8 @@ showShort _ (Builtin_ t) = "##" <> Text.unpack t
showShort numHashChars (DerivedPrivate_ id) = "#" <> take numHashChars (show id)
instance Show Reference where
show (Builtin_ t) = Text.unpack t
show (DerivedPrivate_ id) = "#" <> show id
show (Builtin_ t) = "##" <> Text.unpack t
show (DerivedPrivate_ id) = "#" <> show id
instance Hashable.Hashable Reference where
tokens (Builtin_ txt) = [Hashable.Tag 0, Hashable.Text txt]

View File

@ -1,12 +1,16 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Unison.Referent where
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Word (Word64)
import Unison.Hashable (Hashable)
import qualified Unison.Hashable as H
import Unison.Reference (Reference)
import qualified Unison.Reference as R
import Data.Word (Word64)
data Referent = Ref Reference | Req Reference Int | Con Reference Int
deriving (Show, Ord, Eq)
@ -16,14 +20,21 @@ data Referent = Ref Reference | Req Reference Int | Con Reference Int
showShort :: Int -> Referent -> String
showShort numHashChars r = case r of
Ref r -> R.showShort numHashChars r
Ref r -> R.showShort numHashChars r
Con r cid -> R.showShort numHashChars r <> "#" <> show cid
Req r cid -> R.showShort numHashChars r <> "#" <> show cid
toString :: Referent -> String
toString = \case
Ref r -> show r
Con r cid -> show r <> "#" <> show cid
Req r cid -> show r <> "#" <> show cid
isConstructor :: Referent -> Bool
isConstructor (Con _ _) = True
isConstructor (Req _ _) = True
isConstructor _ = False
isConstructor _ = False
toReference :: Referent -> Reference
toReference = \case
@ -37,6 +48,22 @@ toTypeReference = \case
Con r _i -> Just r
_ -> Nothing
-- Parses Asdf##Foo as Builtin Foo
-- Parses Asdf#abc123-1-2 as Derived 'abc123' 1 2
unsafeFromText :: Text -> Referent
unsafeFromText t = case Text.split (=='#') t of
[_, "", b] -> Ref $ R.Builtin b
[_, h] -> Ref $ case Text.split (=='-') h of
[hash] -> R.derivedBase58 hash 0 1
[hash, pos, size] -> R.derivedBase58 hash (read . Text.unpack $ pos)
(read . Text.unpack $ size)
_ -> bail
[_, _h, _cid] -> error . Text.unpack $
"todo: how can we parse a Referent as Con vs Req? " <> t
_ -> bail
where bail = error . Text.unpack $ "couldn't parse a Referent from " <> t
instance Hashable Referent where
tokens (Ref r) = [H.Tag 0] ++ H.tokens r
tokens (Req r i) = [H.Tag 1] ++ H.tokens r ++ H.tokens (fromIntegral i :: Word64)

View File

@ -15,7 +15,7 @@ import Control.Monad.Writer ( WriterT(..)
)
import Data.Maybe
import Data.Sequence ( Seq )
import Unison.Names ( Name )
import Unison.Name ( Name )
import qualified Unison.Parser as Parser
import Unison.Paths ( Path )
import Unison.Term ( AnnotatedTerm )

View File

@ -6,7 +6,6 @@ module Unison.TermPrinter where
import Control.Monad (join)
import Data.List
import qualified Data.Text as Text
import Data.Foldable ( fold
)
import Data.Maybe ( fromMaybe
@ -16,7 +15,11 @@ import Data.Vector ( )
import Text.Read ( readMaybe )
import Unison.ABT ( pattern AbsN' )
import qualified Unison.Blank as Blank
import qualified Unison.HashQualified as HQ
import Unison.Lexer ( symbolyId )
import Unison.Name ( Name )
import qualified Unison.Name as Name
import Unison.NamePrinter ( prettyHashQualified )
import Unison.PatternP ( Pattern )
import qualified Unison.PatternP as Pattern
import qualified Unison.Referent as Referent
@ -121,9 +124,10 @@ pretty
-> Pretty String
pretty n AmbientContext { precedence = p, blockContext = bc, infixContext = ic } term
= specialCases term $ \case
Var' v -> parenIfInfix (Var.nameStr v) ic . PP.text $ Var.name v
Ref' r -> parenIfInfix name ic $ l $ name
where name = Text.unpack (PrettyPrintEnv.termName n (Referent.Ref r))
Var' v -> parenIfInfix name ic . prettyHashQualified $ name
where name = HQ.fromVar v
Ref' r -> parenIfInfix name ic . prettyHashQualified $ name
where name = PrettyPrintEnv.termName n (Referent.Ref r)
Ann' tm t ->
paren (p >= 0)
$ pretty n (ac 10 Normal) tm
@ -142,8 +146,9 @@ pretty n AmbientContext { precedence = p, blockContext = bc, infixContext = ic }
Text' s -> l $ show s
Blank' id -> l "_" <> (l $ fromMaybe "" (Blank.nameb id))
Constructor' ref i ->
l (Text.unpack (PrettyPrintEnv.constructorName n ref i))
Request' ref i -> l (Text.unpack (PrettyPrintEnv.requestName n ref i))
prettyHashQualified $ PrettyPrintEnv.termName n (Referent.Con ref i)
Request' ref i ->
prettyHashQualified $ PrettyPrintEnv.termName n (Referent.Req ref i)
Handle' h body ->
paren (p >= 2)
$ ("handle" `PP.hang` pretty n (ac 2 Normal) h)
@ -214,6 +219,7 @@ pretty n AmbientContext { precedence = p, blockContext = bc, infixContext = ic }
varList vs = sepList' (PP.text . Var.name) PP.softbreak vs
commaList = sepList ("," <> PP.softbreak)
printLet :: Var v => BlockContext -> [(v, AnnotatedTerm v a)] -> AnnotatedTerm v a -> Pretty String
printLet sc bs e =
paren ((sc /= Block) && p >= 12)
$ letIntro
@ -222,7 +228,7 @@ pretty n AmbientContext { precedence = p, blockContext = bc, infixContext = ic }
where
printBinding (v, binding) = if isBlank $ Var.nameStr v
then pretty n (ac (-1) Normal) binding
else prettyBinding n v binding
else prettyBinding n (HQ.fromVar v) binding
letIntro = case sc of
Block -> id
Normal -> \x -> "let" `PP.hang` x
@ -246,7 +252,7 @@ pretty n AmbientContext { precedence = p, blockContext = bc, infixContext = ic }
binaryOpsPred :: Var v => AnnotatedTerm v a -> Bool
binaryOpsPred = \case
Ref' r | isSymbolic (PrettyPrintEnv.termName n (Referent.Ref r)) -> True
Var' v | isSymbolic (Var.name v) -> True
Var' v | isSymbolic (HQ.fromVar v) -> True
_ -> False
nonForcePred :: AnnotatedTerm v a -> Bool
@ -308,11 +314,11 @@ prettyPattern n p vs patt = case patt of
let (pats_printed, tail_vs) = patterns vs pats
in (PP.parenthesizeCommas pats_printed, tail_vs)
Pattern.Constructor _ ref i [] ->
(PP.text (PrettyPrintEnv.patternName n ref i), vs)
(prettyHashQualified (PrettyPrintEnv.patternName n ref i), vs)
Pattern.Constructor _ ref i pats ->
let (pats_printed, tail_vs) = patternsSep PP.softbreak vs pats
in ( paren (p >= 10)
$ PP.text (PrettyPrintEnv.patternName n ref i)
$ prettyHashQualified (PrettyPrintEnv.patternName n ref i)
`PP.hang` pats_printed
, tail_vs)
Pattern.As _ pat ->
@ -325,7 +331,7 @@ prettyPattern n p vs patt = case patt of
Pattern.EffectBind _ ref i pats k_pat ->
let (pats_printed , tail_vs ) = patternsSep PP.softbreak vs pats
(k_pat_printed, eventual_tail) = prettyPattern n 0 tail_vs k_pat
in ("{" <> l (Text.unpack (PrettyPrintEnv.patternName n ref i))
in ("{" <> prettyHashQualified (PrettyPrintEnv.patternName n ref i)
<> (intercalateMap " " id [pats_printed, "->", k_pat_printed]) <>
"}"
, eventual_tail)
@ -355,15 +361,15 @@ a + b = ...
-}
prettyBinding
:: Var v => PrettyPrintEnv -> v -> AnnotatedTerm v a -> Pretty String
prettyBinding n v term = go (symbolic && isBinary term) term where
:: Var v => PrettyPrintEnv -> HQ.HashQualified -> AnnotatedTerm v a -> Pretty String
prettyBinding env v term = go (symbolic && isBinary term) term where
go infix' = \case
Ann' tm tp -> PP.lines [
PP.group (renderName v <> PP.hang " :" (TypePrinter.pretty n (-1) tp)),
PP.group (prettyBinding n v tm) ]
PP.group (renderName v <> PP.hang " :" (TypePrinter.pretty env (-1) tp)),
PP.group (prettyBinding env v tm) ]
LamsNamedOpt' vs body -> PP.group $
PP.group (defnLhs v vs <> " =") `PP.hang`
pretty n (ac (-1) Block) body
pretty env (ac (-1) Block) body
where
t -> l "error: " <> l (show t)
where
@ -371,21 +377,21 @@ prettyBinding n v term = go (symbolic && isBinary term) term where
then case vs of
x : y : _ ->
PP.sep " " [PP.text (Var.name x),
PP.text (Var.name v),
prettyHashQualified v,
PP.text (Var.name y)]
_ -> l "error"
else if null vs then renderName v
else renderName v `PP.hang` args vs
args vs = PP.spacedMap (PP.text . Var.name) vs
renderName v = parenIfInfix (Var.nameStr v) NonInfix $ l (Var.nameStr v)
symbolic = isSymbolic (Var.name v)
renderName n = parenIfInfix n NonInfix $ prettyHashQualified n
symbolic = isSymbolic v
isBinary = \case
Ann' tm _ -> isBinary tm
LamsNamedOpt' vs _ -> length vs == 2
_ -> False -- unhittable
prettyBinding'
:: Var v => Int -> PrettyPrintEnv -> v -> AnnotatedTerm v a -> String
:: Var v => Int -> PrettyPrintEnv -> HQ.HashQualified -> AnnotatedTerm v a -> String
prettyBinding' width n v t = PP.render width $ prettyBinding n v t
paren :: Bool -> Pretty String -> Pretty String
@ -393,9 +399,9 @@ paren True s = PP.group $ "(" <> s <> ")"
paren False s = PP.group s
parenIfInfix
:: String -> InfixContext -> Pretty String -> Pretty String
:: HQ.HashQualified -> InfixContext -> Pretty String -> Pretty String
parenIfInfix name ic =
if isSymbolic (Text.pack name) && ic == NonInfix then paren True else id
if isSymbolic name && ic == NonInfix then paren True else id
l :: String -> Pretty String
l = PP.lit
@ -406,8 +412,13 @@ l = PP.lit
-- When we use imports in rendering, this will need revisiting, so that we can
-- render say 'foo.+ x y' as 'import foo ... x + y'. symbolyId doesn't match
-- 'foo.+', only '+'.
isSymbolic :: Text.Text -> Bool
isSymbolic name = case symbolyId $ Text.unpack $ name of
isSymbolic :: HQ.HashQualified -> Bool
isSymbolic (HQ.NameOnly name) = isSymbolic' name
isSymbolic (HQ.HashQualified name _) = isSymbolic' name
isSymbolic (HQ.HashOnly _) = False
isSymbolic' :: Name -> Bool
isSymbolic' name = case symbolyId . Name.toString $ name of
Right _ -> True
_ -> False

View File

@ -8,7 +8,8 @@ module Unison.TypePrinter where
import Data.Maybe (isJust)
import Data.String (fromString)
import qualified Data.Text as Text
import Unison.Names (Name)
import Unison.HashQualified (HashQualified)
import Unison.NamePrinter (prettyHashQualified)
import Unison.PrettyPrintEnv (PrettyPrintEnv)
import qualified Unison.PrettyPrintEnv as PrettyPrintEnv
import Unison.Reference (pattern Builtin)
@ -46,7 +47,7 @@ pretty :: Var v => PrettyPrintEnv -> Int -> AnnotatedType v a -> Pretty String
-- application has precedence 10.
pretty n p tp = case tp of
Var' v -> l $ Text.unpack (Var.name v)
Ref' r -> l $ Text.unpack (PrettyPrintEnv.typeName n r)
Ref' r -> prettyHashQualified $ (PrettyPrintEnv.typeName n r)
Cycle' _ _ -> l $ "error" -- TypeParser does not currently emit Cycle
Abs' _ -> l $ "error" -- TypeParser does not currently emit Abs
Ann' _ _ -> l $ "error" -- TypeParser does not currently emit Ann
@ -112,24 +113,46 @@ pretty' Nothing n t = PP.render maxBound $ pretty n (-1) t
prettySignatures'
:: Var v => PrettyPrintEnv
-> [(Name, AnnotatedType v a)]
-> [(HashQualified, AnnotatedType v a)]
-> [Pretty ColorText]
prettySignatures' env ts = PP.align
[ (PP.text name, (": " <> PP.map fromString (pretty env (-1) typ)) `PP.orElse`
[ (prettyHashQualified name, (": " <> PP.map fromString (pretty env (-1) typ)) `PP.orElse`
(": " <> PP.indentNAfterNewline 2 (PP.map fromString (pretty env (-1) typ))))
| (name, typ) <- ts
]
prettySignaturesAlt'
:: Var v => PrettyPrintEnv
-> [([HashQualified], AnnotatedType v a)]
-> [Pretty ColorText]
prettySignaturesAlt' env ts = PP.align
[ (PP.commas . fmap prettyHashQualified $ names, (": " <> PP.map fromString (pretty env (-1) typ)) `PP.orElse`
(": " <> PP.indentNAfterNewline 2 (PP.map fromString (pretty env (-1) typ))))
| (names, typ) <- ts
]
-- prettySignatures'' :: Var v => PrettyPrintEnv -> [(Name, AnnotatedType v a)] -> [Pretty ColorText]
-- prettySignatures'' env ts = prettySignatures' env (first HQ.fromName <$> ts)
prettySignatures
:: Var v
=> PrettyPrintEnv
-> [(Name, AnnotatedType v a)]
-> [(HashQualified, AnnotatedType v a)]
-> Pretty ColorText
prettySignatures env ts = PP.lines $
PP.group <$> prettySignatures' env ts
prettyDataHeader :: Name -> Pretty ColorText
prettyDataHeader name = PP.bold "type " <> PP.text name
prettySignaturesAlt
:: Var v
=> PrettyPrintEnv
-> [([HashQualified], AnnotatedType v a)]
-> Pretty ColorText
prettySignaturesAlt env ts = PP.lines $
PP.group <$> prettySignaturesAlt' env ts
prettyEffectHeader :: Name -> Pretty ColorText
prettyEffectHeader name = PP.bold "ability " <> PP.text name
prettyDataHeader :: HashQualified -> Pretty ColorText
prettyDataHeader name = PP.bold "type " <> prettyHashQualified name
prettyEffectHeader :: HashQualified -> Pretty ColorText
prettyEffectHeader name = PP.bold "ability " <> prettyHashQualified name

View File

@ -32,7 +32,8 @@ import Data.Text (Text)
import qualified Data.Text as Text
import qualified Unison.ABT as ABT
import qualified Unison.Blank as B
import Unison.Names (Name)
-- import Unison.Name (Name)
-- import qualified Unison.Name as Name
import Unison.Referent (Referent)
import Unison.Result (pattern Result, Result,
ResultT, runResultT)
@ -47,6 +48,8 @@ import qualified Unison.Var as Var
import qualified Unison.Typechecker.TypeLookup as TL
-- import Debug.Trace
type Name = Text
type Term v loc = AnnotatedTerm v loc
type Type v loc = AnnotatedType v loc

View File

@ -19,6 +19,7 @@ import Unison.DataDeclaration (DataDeclaration')
import Unison.DataDeclaration (EffectDeclaration' (..))
import Unison.DataDeclaration (hashDecls, toDataDecl, withEffectDecl)
import qualified Unison.DataDeclaration as DD
import qualified Unison.Name as Name
import Unison.Names (Names)
import qualified Unison.Names as Names
import Unison.Reference (Reference)
@ -104,7 +105,7 @@ dependencies uf ns = directReferences <>
tm = term uf
directReferences = Term.dependencies tm
freeTypeVarRefs = -- we aren't doing any special resolution for types
catMaybes (flip Map.lookup (Names.typeNames ns) . Var.name <$>
catMaybes (flip Map.lookup (Names.typeNames ns) . Name.unsafeFromVar <$>
Set.toList (Term.freeTypeVars tm))
-- foreach name in Names.termNames,
-- if the name or unqualified name is in Term.freeVars,
@ -112,8 +113,8 @@ dependencies uf ns = directReferences <>
freeTermVarRefs =
[ Referent.toReference referent
| (name, referent) <- Map.toList $ Names.termNames ns
, Var.named name `Set.member` Term.freeVars tm
|| Var.unqualified (Var.named name) `Set.member` Term.freeVars tm
, Name.toVar name `Set.member` Term.freeVars tm
|| Var.unqualified (Name.toVar name) `Set.member` Term.freeVars tm
]
discardTypes :: AnnotatedTerm v a -> TypecheckedUnisonFile v a -> UnisonFile v a
@ -220,8 +221,8 @@ environmentFor
environmentFor names0 dataDecls0 effectDecls0 =
let
-- ignore builtin types that will be shadowed by user-defined data/effects
unshadowed n = Map.notMember (Var.named n) dataDecls0
&& Map.notMember (Var.named n) effectDecls0
unshadowed n = Map.notMember (Name.toVar n) dataDecls0
&& Map.notMember (Name.toVar n) effectDecls0
names = Names.filterTypes unshadowed names0
-- data decls and hash decls may reference each other, and thus must be hashed together
dataDecls :: Map v (DataDeclaration' v a)

View File

@ -0,0 +1,2 @@
module Unison.Util.Histogram where
--

View File

@ -9,6 +9,10 @@ intercalateMap :: (Foldable t, Monoid a) => a -> (b -> a) -> t b -> a
intercalateMap separator renderer elements =
mconcat $ intersperse separator (renderer <$> toList elements)
fromMaybe :: Monoid a => Maybe a -> a
fromMaybe Nothing = mempty
fromMaybe (Just a) = a
whenM :: Monoid a => Bool -> a -> a
whenM True a = a
whenM False _ = mempty

View File

@ -32,7 +32,6 @@ module Unison.Util.Pretty (
lit,
map,
nest,
name,
newline,
numbered,
orElse,
@ -291,10 +290,6 @@ align' rows = alignedRows
text :: IsString s => Text -> Pretty s
text t = fromString (Text.unpack t)
name :: (Pretty ColorText -> Pretty ColorText) -> Text -> Pretty ColorText
name style t = case Text.span (/= '#') t of
(hd, tl) -> group $ style (text hd) <> text tl
hang'
:: (LL.ListLike s Char, IsString s)
=> Pretty s

View File

@ -5,6 +5,7 @@ module Unison.Test.TermPrinter where
import EasyTest
import qualified Data.Text as Text
import Unison.ABT (annotation)
import qualified Unison.HashQualified as HQ
import Unison.Term
import Unison.TermPrinter
import qualified Unison.Type as Type
@ -69,7 +70,7 @@ tc_binding width v mtp tm expected =
input_term (Just (tp)) = ann (annotation tp) base_term tp
input_term Nothing = base_term
var_v = symbol $ Text.pack v
prettied = prettyBinding get_names var_v (input_term input_type)
prettied = prettyBinding get_names (HQ.fromVar var_v) (input_term input_type)
actual = if width == 0
then PP.renderUnbroken $ prettied
else PP.render width $ prettied

View File

@ -41,7 +41,6 @@ library
Unison.Codebase
Unison.Codebase.Branch
Unison.Codebase.Causal
Unison.Codebase.CommandLine
Unison.Codebase.CommandLine2
Unison.Codebase.Editor
Unison.Codebase.Editor.Actions
@ -59,9 +58,12 @@ library
Unison.FileParsers
Unison.Hash
Unison.Hashable
Unison.HashQualified
Unison.Kind
Unison.Lexer
Unison.Name
Unison.Names
Unison.NamePrinter
Unison.Parser
Unison.Parsers
Unison.Path
@ -95,6 +97,7 @@ library
Unison.Util.Components
Unison.Util.Free
Unison.Util.Logger
Unison.Util.Histogram
Unison.Util.Menu
Unison.Util.Monoid
Unison.Util.Pretty
@ -193,15 +196,6 @@ executable prettyprintdemo
text,
unison-parser-typechecker
executable unisonold
main-is: Main.hs
hs-source-dirs: unisonold
ghc-options: -Wall -fno-warn-name-shadowing -fno-warn-missing-pattern-synonym-signatures
build-depends:
base,
safe,
unison-parser-typechecker
executable tests
main-is: Suite.hs
ghc-options: -W -fno-warn-name-shadowing -fno-warn-missing-pattern-synonym-signatures -threaded -rtsopts -with-rtsopts=-N -v0

View File

@ -1,52 +0,0 @@
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Char ( toLower )
import Safe ( headMay )
import System.Environment ( getArgs )
import System.IO ( BufferMode(NoBuffering)
, hSetBuffering
, stdout
)
import qualified Unison.Codebase as Codebase
import qualified Unison.Codebase.CommandLine as CommandLine
import qualified Unison.Codebase.FileCodebase as FileCodebase
import Unison.Codebase.Runtime.JVM ( javaRuntime )
import qualified Unison.Codebase.Serialization as S
import Unison.Codebase.Serialization.V0
( formatSymbol
, getSymbol
)
import Unison.Parser ( Ann(External) )
main :: IO ()
main = do
args <- getArgs
hSetBuffering stdout NoBuffering -- cool
let codebasePath = ".unison"
initialBranchName = "master"
scratchFilePath = "."
theCodebase =
FileCodebase.codebase1 External formatSymbol formatAnn codebasePath
launch = CommandLine.main scratchFilePath
initialBranchName
(headMay args)
(javaRuntime getSymbol 42441)
theCodebase
exists <- FileCodebase.exists codebasePath
case exists of
True -> launch
False -> do
putStr
"I can't find a Unison codebase here, would you like to create one? [y/n] "
line <- getLine
case words (map toLower line) of
('y' : _) : _ -> do
FileCodebase.initialize codebasePath
Codebase.initialize theCodebase
launch
_ -> pure ()
formatAnn :: S.Format Ann
formatAnn = S.Format (pure External) (\_ -> pure ())