This commit is contained in:
Chris Penner 2022-01-14 11:52:45 -06:00
parent 191ad5be40
commit 0a960c454d
2 changed files with 247 additions and 125 deletions

View File

@ -55,9 +55,8 @@ import qualified Unison.Codebase.Editor.Propagate as Propagate
import Unison.Codebase.Editor.RemoteRepo (ReadRemoteNamespace, WriteRemotePath, WriteRepo, printNamespace, writePathToRead, writeToRead)
import Unison.Codebase.Editor.SlurpComponent (SlurpComponent (..))
import qualified Unison.Codebase.Editor.SlurpComponent as SC
import qualified Unison.Codebase.Editor.Slurp as Slurp
import Unison.Codebase.Editor.SlurpResult (SlurpResult (..))
-- import qualified Unison.Codebase.Editor.SlurpResult as Slurp
import qualified Unison.Codebase.Editor.SlurpResult as Slurp
import qualified Unison.Codebase.Editor.TodoOutput as TO
import qualified Unison.Codebase.Editor.UriParser as UriParser
import qualified Unison.Codebase.MainTerm as MainTerm
@ -152,8 +151,6 @@ import qualified Data.Set.NonEmpty as NESet
import Data.Set.NonEmpty (NESet)
import Unison.Symbol (Symbol)
import qualified Unison.Codebase.Editor.Input as Input
import Debug.Pretty.Simple
import qualified Unison.Codebase.Editor.SlurpResult as OldSlurp
defaultPatchNameSegment :: NameSegment
defaultPatchNameSegment = "patch"
@ -262,12 +259,11 @@ loop = do
loadUnisonFile sourceName text = do
let lexed = L.lexer (Text.unpack sourceName) (Text.unpack text)
withFile [] sourceName (text, lexed) $ \unisonFile -> do
sr <- Slurp.analyzeTypecheckedUnisonFile unisonFile <$> currentPathNames
-- sr <- toSlurpResult currentPath' unisonFile <$> slurpResultNames
sr <- toSlurpResult currentPath' unisonFile <$> slurpResultNames
names <- displayNames unisonFile
pped <- prettyPrintEnvDecl names
let ppe = PPE.suffixifiedPPE pped
eval . Notify $ Typechecked sourceName ppe (undefined $ Slurp.toSlurpPrintout sr) unisonFile
eval . Notify $ Typechecked sourceName ppe sr unisonFile
unlessError' EvaluationFailure do
(bindings, e) <- ExceptT . eval . Evaluate ppe $ unisonFile
lift do
@ -1259,11 +1255,11 @@ loop = do
Nothing -> respond NoUnisonFile
Just uf -> do
sr <-
OldSlurp.disallowUpdates
Slurp.disallowUpdates
. applySelection hqs uf
. toSlurpResult currentPath' uf
<$> slurpResultNames
let adds = OldSlurp.adds sr
let adds = Slurp.adds sr
stepAtNoSync Branch.CompressHistory (Path.unabsolute currentPath', doSlurpAdds adds uf)
eval . AddDefsToCodebase . filterBySlurpResult sr $ uf
ppe <- prettyPrintEnvDecl =<< displayNames uf
@ -1273,7 +1269,7 @@ loop = do
PreviewAddI hqs -> case (latestFile', uf) of
(Just (sourceName, _), Just uf) -> do
sr <-
OldSlurp.disallowUpdates
Slurp.disallowUpdates
. applySelection hqs uf
. toSlurpResult currentPath' uf
<$> slurpResultNames
@ -1823,49 +1819,50 @@ handleUpdate input maybePatchPath hqs = do
let patchPath = fromMaybe defaultPatchPath maybePatchPath
slurpCheckNames <- slurpResultNames
let currentPathNames = slurpCheckNames
let sr = Slurp.analyzeTypecheckedUnisonFile uf currentPathNames
-- let sr :: SlurpResult v
-- sr =
-- applySelection hqs uf
-- . toSlurpResult currentPath' uf
-- $ slurpCheckNames
let sr :: SlurpResult v
sr =
applySelection hqs uf
. toSlurpResult currentPath' uf
$ slurpCheckNames
addsAndUpdates :: SlurpComponent v
addsAndUpdates = Slurp.updates sr <> Slurp.adds sr
fileNames :: Names
fileNames = UF.typecheckedToNames uf
-- todo: display some error if typeEdits or termEdits itself contains a loop
-- typeEdits :: Map Name (Reference, Reference)
-- typeEdits = Map.fromList $ map f (toList $ SC.types (updates sr))
-- where
-- f v = case ( toList (Names.typesNamed slurpCheckNames n),
-- toList (Names.typesNamed fileNames n)
-- ) of
-- ([old], [new]) -> (n, (old, new))
-- _ ->
-- error $
-- "Expected unique matches for "
-- ++ Var.nameStr v
-- ++ " but got: "
-- ++ show otherwise
-- where
-- n = Name.unsafeFromVar v
typeEdits :: Map Name (Reference, Reference)
typeEdits = Map.fromList $ map f (toList $ SC.types (updates sr))
where
f v = case ( toList (Names.typesNamed slurpCheckNames n),
toList (Names.typesNamed fileNames n)
) of
([old], [new]) -> (n, (old, new))
_ ->
error $
"Expected unique matches for "
++ Var.nameStr v
++ " but got: "
++ show otherwise
where
n = Name.unsafeFromVar v
hashTerms :: Map Reference (Type v Ann)
hashTerms = Map.fromList (toList hashTerms0)
where
hashTerms0 = (\(r, _wk, _tm, typ) -> (r, typ)) <$> UF.hashTerms uf
-- termEdits :: Map Name (Reference, Reference)
-- termEdits = Map.fromList $ map g (toList $ SC.terms (updates sr))
-- where
-- g v = case ( toList (Names.refTermsNamed slurpCheckNames n),
-- toList (Names.refTermsNamed fileNames n)
-- ) of
-- ([old], [new]) -> (n, (old, new))
-- _ ->
-- error $
-- "Expected unique matches for "
-- ++ Var.nameStr v
-- ++ " but got: "
-- ++ show otherwise
-- where
-- n = Name.unsafeFromVar v
termEdits :: Map Name (Reference, Reference)
termEdits = Map.fromList $ map g (toList $ SC.terms (updates sr))
where
g v = case ( toList (Names.refTermsNamed slurpCheckNames n),
toList (Names.refTermsNamed fileNames n)
) of
([old], [new]) -> (n, (old, new))
_ ->
error $
"Expected unique matches for "
++ Var.nameStr v
++ " but got: "
++ show otherwise
where
n = Name.unsafeFromVar v
termDeprecations :: [(Name, Referent)]
termDeprecations =
[ (n, r)
@ -1922,28 +1919,19 @@ handleUpdate input maybePatchPath hqs = do
updatePatches :: Branch0 m -> m (Branch0 m)
updatePatches = Branch.modifyPatches seg updatePatch
case Slurp.slurpOp Update (undefined hqs) sr of
Left errs -> undefined
Right (adds, updates) ->
-- when nonEmpty
-- doSlurpUpdates updates
-- doSlurpAdds adds
undefined adds updates
-- take a look at the `updates` from the SlurpResult
-- and make a patch diff to record a replacement from the old to new references
-- stepManyAtMNoSync Branch.CompressHistory
-- [ ( Path.unabsolute currentPath',
-- pure . doSlurpUpdates typeEdits termEdits termDeprecations
-- ),
-- ( Path.unabsolute currentPath',
-- pure . doSlurpAdds addsAndUpdates uf
-- ),
-- (Path.unabsolute p, updatePatches)
-- ]
-- eval . AddDefsToCodebase . filterBySlurpResult sr $ uf
-- when (Slurp.isNonempty sr) $ do
when (Slurp.isNonempty sr) $ do
-- take a look at the `updates` from the SlurpResult
-- and make a patch diff to record a replacement from the old to new references
stepManyAtMNoSync Branch.CompressHistory
[ ( Path.unabsolute currentPath',
pure . doSlurpUpdates typeEdits termEdits termDeprecations
),
( Path.unabsolute currentPath',
pure . doSlurpAdds addsAndUpdates uf
),
(Path.unabsolute p, updatePatches)
]
eval . AddDefsToCodebase . filterBySlurpResult sr $ uf
ppe <- prettyPrintEnvDecl =<< displayNames uf
respond $ SlurpOutput input (PPE.suffixifiedPPE ppe) sr
-- propagatePatch prints TodoOutput
@ -2794,8 +2782,8 @@ toSlurpResult ::
UF.TypecheckedUnisonFile v Ann ->
Names ->
SlurpResult v
toSlurpResult curPath uf existingNames = pTraceShowId $
OldSlurp.subtractComponent (conflicts <> ctorCollisions) $
toSlurpResult curPath uf existingNames =
Slurp.subtractComponent (conflicts <> ctorCollisions) $
SlurpResult
uf
mempty
@ -2901,13 +2889,13 @@ toSlurpResult curPath uf existingNames = pTraceShowId $
R.Relation Name Referent ->
R.Relation Name Referent ->
Set v ->
Map v OldSlurp.Aliases
Map v Slurp.Aliases
buildAliases existingNames namesFromFile duplicates =
Map.fromList
[ ( var n,
if null aliasesOfOld
then OldSlurp.AddAliases aliasesOfNew
else OldSlurp.UpdateAliases aliasesOfOld aliasesOfNew
then Slurp.AddAliases aliasesOfNew
else Slurp.UpdateAliases aliasesOfOld aliasesOfNew
)
| (n, r@Referent.Ref {}) <- R.toList namesFromFile,
-- All the refs whose names include `n`, and are not `r`
@ -2922,14 +2910,14 @@ toSlurpResult curPath uf existingNames = pTraceShowId $
Set.notMember (var n) duplicates
]
termAliases :: Map v OldSlurp.Aliases
termAliases :: Map v Slurp.Aliases
termAliases =
buildAliases
(Names.terms existingNames)
(Names.terms fileNames)
(SC.terms dups)
typeAliases :: Map v OldSlurp.Aliases
typeAliases :: Map v Slurp.Aliases
typeAliases =
buildAliases
(R.mapRan Referent.Ref $ Names.types existingNames)
@ -3081,7 +3069,7 @@ doSlurpAdds slurp uf = Branch.batchUpdates (typeActions <> termActions)
typeActions = map doType . toList $ SC.types slurp
termActions =
map doTerm . toList $
SC.terms slurp <> OldSlurp.constructorsFor (SC.types slurp) uf
SC.terms slurp <> Slurp.constructorsFor (SC.types slurp) uf
names = UF.typecheckedToNames uf
tests = Set.fromList $ fst <$> UF.watchesOfKind WK.TestWatch (UF.discardTypes uf)
(isTestType, isTestValue) = isTest

View File

@ -1,10 +1,9 @@
{-# OPTIONS_GHC -Wno-unused-local-binds #-}
module Unison.Codebase.Editor.Slurp where
import Control.Lens
import Control.Monad.State
import qualified Data.List.NonEmpty as NEList
import qualified Data.Map as Map
import qualified Data.Semigroup.Foldable as Semigroup
import qualified Data.Set as Set
import qualified Unison.DataDeclaration as DD
import Unison.Hash (Hash)
@ -16,12 +15,10 @@ import qualified Unison.Names as Names
import Unison.Parser.Ann (Ann)
import Unison.Prelude
import qualified Unison.Reference as Ref
import qualified Unison.Referent as Referent
import qualified Unison.Referent' as Referent
import Unison.Term (Term)
import qualified Unison.Term as Term
import qualified Unison.UnisonFile as UF
import qualified Unison.UnisonFile.Names as UF
import qualified Unison.Util.Relation as Rel
import qualified Unison.Util.Relation3 as Rel3
import qualified Unison.Util.Set as Set
@ -45,8 +42,24 @@ data LabeledVar v = LabeledVar v LD.LabeledDependency
data SlurpStatus = New | Updated | Duplicate
deriving (Eq, Ord, Show)
data SlurpOp = Add | Update
deriving (Eq, Ord, Show)
data BlockStatus v
= Add
| Duplicated
| NeedsUpdate v
| ErrFrom v SlurpErr
| SelfErr SlurpErr
deriving (Eq, Ord)
instance Semigroup (BlockStatus v) where
SelfErr err <> _ = SelfErr err
_ <> SelfErr err = SelfErr err
ErrFrom v err <> _ = ErrFrom v err
_ <> ErrFrom v err = ErrFrom v err
NeedsUpdate v <> _ = NeedsUpdate v
_ <> NeedsUpdate v = NeedsUpdate v
Add <> _ = Add
_ <> Add = Add
Duplicated <> Duplicated = Duplicated
data TypeOrTermVar v = TypeVar v | TermVar v
deriving (Eq, Ord, Show)
@ -81,18 +94,87 @@ data SlurpErr
| CtorTermCollision
deriving (Eq, Ord, Show)
data SlurpComponent v = SlurpComponent {types :: Set v, terms :: Set v, errs :: Map v SlurpErr}
data SlurpComponent v = SlurpComponent {scTypes :: Set v, scTerms :: Set v}
deriving (Eq, Ord, Show)
instance Ord v => Semigroup (SlurpComponent v) where
SlurpComponent typeL termL <> SlurpComponent typeR termR =
SlurpComponent (typeL <> typeR) (termL <> termR)
instance Ord v => Monoid (SlurpComponent v) where
mempty = SlurpComponent mempty mempty
data DefinitionNotes
= DefStatus SlurpStatus
= DefOk SlurpStatus
| DefErr SlurpErr
data SlurpResult v = SlurpResult
{ termNotes :: Map v (DefinitionNotes, Set (LabeledVar v)),
typeNotes :: Map v (DefinitionNotes, Set (LabeledVar v))
{ termNotes :: Map v (DefinitionNotes, Set (TypeOrTermVar v)),
typeNotes :: Map v (DefinitionNotes, Set (TypeOrTermVar v))
}
type Result v = Map (BlockStatus v) (SlurpComponent v)
-- data Result v = Result
-- { addable :: SlurpComponent v,
-- needUpdate :: SlurpComponent v,
-- duplicate :: SlurpComponent v,
-- blockedTerms :: Map (SlurpErr v) (Set v)
-- }
-- instance Semigroup (Result v) where
-- Result adds1 updates1 duplicates1 tcColl1 ctColl1 <> Result adds2 updates2 duplicates2 tcColl2 ctColl2 =
-- Result (adds1 <> adds2) (updates1 <> updates2) (duplicates1 <> duplicates2) (tcColl1 <> tcColl2) (ctColl1 <> ctColl2)
-- instance Monoid (Result v) where
-- mempty = Result mempty mempty mempty mempty mempty
-- Compute all definitions which can be added, or the reasons why a def can't be added.
results :: forall v. Ord v => SlurpResult v -> Result v
results sr@(SlurpResult terms types) =
Map.unionWith (<>) analyzedTerms analyzedTypes
where
analyzedTerms :: Map (BlockStatus v) (SlurpComponent v)
analyzedTerms =
terms
& Map.toList
& fmap
( \(v, (_, deps)) ->
( Semigroup.foldMap1 (getBlockStatus sr) (TermVar v NEList.:| Set.toList deps),
mempty {scTerms = Set.singleton v}
)
)
& Map.fromListWith (<>)
analyzedTypes :: Map (BlockStatus v) (SlurpComponent v)
analyzedTypes =
types
& Map.toList
& fmap
( \(v, (_, deps)) ->
( Semigroup.foldMap1 (getBlockStatus sr) (TypeVar v NEList.:| Set.toList deps),
mempty {scTypes = Set.singleton v}
)
)
& Map.fromListWith (<>)
getBlockStatus :: Ord v => SlurpResult v -> TypeOrTermVar v -> BlockStatus v
getBlockStatus (SlurpResult {termNotes, typeNotes}) tv =
let v = unlabeled tv
defNotes = case tv of
TypeVar v -> typeNotes Map.! v
TermVar v -> termNotes Map.! v
in case fst defNotes of
DefOk Updated -> NeedsUpdate v
DefErr err -> ErrFrom v err
DefOk New -> Add
DefOk Duplicate -> Duplicated
-- Need to know:
-- What can be added without errors?
-- What can be updated without errors?
-- What has errors?
-- What is blocked?
type ComponentHash = Hash
data Components v = Components
@ -100,18 +182,25 @@ data Components v = Components
typeComponents :: Map Hash (Set v)
}
-- groupByOp :: SlurpResult v -> (SlurpComponent v, SlurpComponent v)
-- groupByOp (SlurpResult terms types) =
-- terms
-- & Map.mapEither (\(notes, deps) ->
-- any (== )
-- )
analyzeTypecheckedUnisonFile ::
forall v.
Var v =>
UF.TypecheckedUnisonFile v Ann ->
Names ->
Maybe (Set v) ->
Names ->
SlurpResult v
analyzeTypecheckedUnisonFile uf unalteredCodebaseNames maybeDefsToConsider =
analyzeTypecheckedUnisonFile uf maybeDefsToConsider unalteredCodebaseNames =
let allInvolvedVars :: Set (LabeledVar v)
allInvolvedVars = foldMap transitiveVarDeps defsToConsider
termStatuses, typeStatuses :: Map v (DefinitionNotes, Set (LabeledVar v))
termStatuses, typeStatuses :: Map v (DefinitionNotes, Set (TypeOrTermVar v))
(termStatuses, typeStatuses) =
allInvolvedVars
& Set.toList
@ -126,12 +215,21 @@ analyzeTypecheckedUnisonFile uf unalteredCodebaseNames maybeDefsToConsider =
LabeledVar _ (LD.TermReferent {}) -> Right x
)
& over both (Map.mapKeys (\(LabeledVar v _) -> v))
& over
both
( Map.map
( fmap
( Set.map
( \case
LabeledVar v (LD.TermReferent {}) -> TermVar v
LabeledVar v (LD.TypeReference {}) -> TypeVar v
)
)
)
)
in -- & Map.mapEitherWithKey _
SlurpResult termStatuses typeStatuses
where
fileNames :: Names
fileNames = UF.typecheckedToNames uf
transitiveCHDeps :: Map ComponentHash (Set ComponentHash)
transitiveCHDeps =
componentTransitiveDeps uf
@ -173,20 +271,36 @@ analyzeTypecheckedUnisonFile uf unalteredCodebaseNames maybeDefsToConsider =
definitionStatus (LabeledVar v ld) =
let existingTypesAtName = Names.typesNamed codebaseNames (Name.unsafeFromVar v)
existingTermsAtName = Names.termsNamed codebaseNames (Name.unsafeFromVar v)
existingTermsMatchingReference = Names.termsNamed codebaseNames (Name.unsafeFromVar v)
in case ld of
LD.TypeReference {} ->
case Set.toList existingTypesAtName of
[] -> DefOk New
[r]
| LD.typeRef r == ld -> DefOk Duplicate
| otherwise -> DefOk Updated
-- If there are many existing terms, they must be in conflict, we can update
-- to resolve the conflict.
_ -> DefOk Updated
LD.TermReference {} ->
case Set.toList existingTermsAtName of
[] -> DefStatus New
[r] | LD.referent r == ld -> DefStatus Duplicate
[Referent.Con {}] | LD.ConReference {} <- ld -> DefErr TermCtorCollision
[Referent.Ref {}] | LD.ConReference {} <- ld -> DefErr CtorTermCollision
-- This allows us to resolve conflicts with an update.
_ -> DefStatus Updated
-- [r] -> DefStatus Updated
-- _ -> DefStatus Conflicted
LD.TermReference {} -> undefined
LD.ConReference {} -> undefined
[] -> DefOk New
rs | any Referent.isConstructor rs -> DefErr TermCtorCollision
[r]
| LD.referent r == ld -> DefOk Duplicate
| otherwise -> DefOk Updated
-- If there are many existing terms, they must be in conflict, we can update
-- to resolve the conflict.
_ -> DefOk Updated
LD.ConReference {} ->
case Set.toList existingTermsAtName of
[] -> DefOk New
rs | any (not . Referent.isConstructor) rs -> DefErr CtorTermCollision
[r]
| LD.referent r == ld -> DefOk Duplicate
| otherwise -> DefOk Updated
-- If there are many existing terms, they must be in conflict, we can update
-- to resolve the conflict.
_ -> DefOk Updated
varRelation :: Rel3.Relation3 v (LabeledVar v) ComponentHash
varRelation = labelling uf
@ -231,27 +345,27 @@ slurpErrs (SlurpResult defs _) =
_ -> Nothing
)
slurpOp ::
forall v.
Ord v =>
SlurpResult v ->
(SlurpComponent v, SlurpComponent v)
slurpOp (SlurpResult terms types) =
let (termAdds, termUpdates, termErrs) = partition terms
(typeAdds, typeUpdates, typeErrs) = partition types
in (SlurpComponent termAdds termUpdates termErrs, SlurpComponent typeAdds typeUpdates typeErrs)
where
partition :: (Map v (DefinitionNotes, Set (LabeledVar v))) -> (Set v, Set v, Map v SlurpErr)
partition sr =
let (adds, updates, errs) =
flip execState mempty $
for (Map.toList sr) $ \(v, (dn, _)) -> do
case dn of
DefStatus New -> _1 %= Set.insert v
DefStatus Updated -> _2 %= Set.insert v
DefStatus Duplicate -> pure ()
DefErr err -> _3 . at v ?= err
in (adds, updates, errs)
-- slurpOp ::
-- forall v.
-- Ord v =>
-- SlurpResult v ->
-- (SlurpComponent v, SlurpComponent v)
-- slurpOp (SlurpResult terms types) =
-- let (termAdds, termUpdates, termErrs) = partition terms
-- (typeAdds, typeUpdates, typeErrs) = partition types
-- in (SlurpComponent termAdds termUpdates termErrs, SlurpComponent typeAdds typeUpdates typeErrs)
-- where
-- partition :: (Map v (DefinitionNotes, Set (LabeledVar v))) -> (Set v, Set v, Map v SlurpErr)
-- partition sr =
-- let (adds, updates, errs) =
-- flip execState mempty $
-- for (Map.toList sr) $ \(v, (dn, _)) -> do
-- case dn of
-- DefOk New -> _1 %= Set.insert v
-- DefOk Updated -> _2 %= Set.insert v
-- DefOk Duplicate -> pure ()
-- DefErr err -> _3 . at v ?= err
-- in (adds, updates, errs)
componentTransitiveDeps :: Ord v => UF.TypecheckedUnisonFile v a -> Map ComponentHash (Set ComponentHash)
componentTransitiveDeps uf =
@ -335,4 +449,24 @@ labelling uf = decls <> effects <> terms
idToComponentHash :: Ref.Id -> ComponentHash
idToComponentHash (Ref.Id componentHash _ _) = componentHash
-- selectVars :: SlurpComponent (LabeledVar v) -> UF.TypecheckedUnisonFile v a -> UF.TypecheckedUnisonFile v a
-- selectVars
-- vs
-- ( UF.TypecheckedUnisonFileId
-- dataDeclarations'
-- effectDeclarations'
-- topLevelComponents'
-- watchComponents
-- hashTerms
-- ) =
-- UF.TypecheckedUnisonFileId datas effects tlcs watches hashTerms'
-- where
-- keepTypes = SC.types keep
-- hashTerms' = Map.restrictKeys hashTerms keepTerms
-- datas = Map.restrictKeys dataDeclarations' keepTypes
-- effects = Map.restrictKeys effectDeclarations' keepTypes
-- tlcs = filter (not . null) $ fmap (List.filter filterTLC) topLevelComponents'
-- watches = filter (not . null . snd) $ fmap (second (List.filter filterTLC)) watchComponents
-- filterTLC (v, _, _) = Set.member v keepTerms
-- dependencyMap :: UF.TypecheckedUnisonFile -> Map