Merge remote-tracking branch 'origin/trunk' into lsp/binding-annotations

This commit is contained in:
Chris Penner 2023-07-05 10:57:10 -06:00
commit f51840e6fd
83 changed files with 2057 additions and 1511 deletions

View File

@ -1,7 +1,5 @@
**Choose your PR title well:** Your pull request title is what's used to create release notes, so please make it descriptive of the change itself, which may be different from the initial motivation to make the change.
Note: CI will check that all code has been formatted with Ormolu. See [development.markdown](https://github.com/unisonweb/unison/blob/trunk/development.markdown) for details of how to set this up.
## Overview
What does this change accomplish and why?
@ -18,12 +16,12 @@ How does it accomplish it, in broad strokes? i.e. How does it change the Haskell
## Interesting/controversial decisions
Include anything that you thought twice about, debated, chose arbitrarily, etc.
Include anything that you thought twice about, debated, chose arbitrarily, etc.
What could have been done differently, but wasn't? And why?
## Test coverage
Have you included tests (which could be a transcript) for this change, or is it somehow covered by existing tests?
Have you included tests (which could be a transcript) for this change, or is it somehow covered by existing tests?
Would you recommend improving the test coverage (either as part of this PR or as a separate issue) or do you think its adequate?

View File

@ -22,11 +22,25 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v37
with:
path: unison
- uses: mrkkrp/ormolu-action@v11
# globs copied from default settings for run-ormolu
files: |
**/*.hs
**/*.hs-boot
separator: "\n"
- uses: unisonweb/run-ormolu@8e1437595b4127e368c114cabd1aeedb3b873918
with:
version: "0.5.0.1"
mode: inplace
pattern: ${{ steps.changed-files.outputs.all_changed_files }}
- name: apply formatting changes
uses: stefanzweifel/git-auto-commit-action@v4
if: ${{ always() }}
with:
commit_message: automatically run ormolu
build:
name: ${{ matrix.os }}
@ -34,7 +48,6 @@ jobs:
needs: ormolu
defaults:
run:
working-directory: unison
shell: bash
strategy:
# Run each build to completion, regardless of if any have failed
@ -47,8 +60,6 @@ jobs:
- windows-2019
steps:
- uses: actions/checkout@v2
with:
path: unison
# The number towards the beginning of the cache keys allow you to manually avoid using a previous cache.
# GitHub will automatically delete caches that haven't been accessed in 7 days, but there is no way to
@ -121,14 +132,14 @@ jobs:
# The installation process differs by OS.
- name: install stack (Linux)
if: runner.os == 'Linux'
working-directory: ${{ github.workspace }}
working-directory: ${{ runner.temp }}
run: |
mkdir stack && cd stack
curl -L https://github.com/commercialhaskell/stack/releases/download/v2.9.1/stack-2.9.1-linux-x86_64.tar.gz | tar -xz
echo "$PWD/stack-"* >> $GITHUB_PATH
- name: install stack (macOS)
working-directory: ${{ github.workspace }}
working-directory: ${{ runner.temp }}
if: runner.os == 'macOS'
run: |
mkdir stack && cd stack
@ -136,7 +147,7 @@ jobs:
echo "$PWD/stack-"* >> $GITHUB_PATH
- name: install stack (windows)
working-directory: ${{ github.workspace }}
working-directory: ${{ runner.temp }}
if: runner.os == 'Windows'
run: |
mkdir stack && cd stack
@ -189,8 +200,14 @@ jobs:
run: stack --no-terminal build --fast --no-run-tests --test
# Run each test suite (tests and transcripts)
- name: check disk space before
if: ${{ always() }}
run: df -h
- name: unison-cli test
run: stack --no-terminal build --fast --test unison-cli
- name: check disk space after
if: ${{ always() }}
run: df -h
- name: unison-core tests
run: stack --no-terminal build --fast --test unison-core
- name: unison-parser-typechecker tests
@ -247,8 +264,8 @@ jobs:
if: runner.os == 'Linux'
with:
path: ~/.cache/unisonlanguage/base.unison
key: base.unison_${{hashFiles('**/unison-src/builtin-tests/base.md')}}-${{github.sha}}
restore-keys: base.unison_${{hashFiles('**/unison-src/builtin-tests/base.md')}}-
key: base.unison_${{hashFiles('**/unison-src/builtin-tests/base.md','**/unison-cli/src/Unison/JitInfo.hs')}}-${{github.sha}}
restore-keys: base.unison_${{hashFiles('**/unison-src/builtin-tests/base.md','**/unison-cli/src/Unison/JitInfo.hs')}}-
- name: set up `base` codebase
if: runner.os == 'Linux'

View File

@ -27,8 +27,6 @@ library:
source-dirs: src
exposed-modules:
- U.Codebase.Sqlite.V2.HashHandle
- U.Codebase.Branch.Hashing
- U.Codebase.Causal.Hashing
when:
- condition: false
other-modules: Paths_unison_codebase_sqlite_hashing_v2

View File

@ -5,8 +5,8 @@ import Data.Set qualified as Set
import U.Codebase.HashTags (BranchHash (..), CausalHash (..))
import Unison.Hashing.V2 qualified as Hashing
hashCausal :: Set CausalHash -> BranchHash -> CausalHash
hashCausal ancestors branchHash =
hashCausal :: BranchHash -> Set CausalHash -> CausalHash
hashCausal branchHash ancestors =
CausalHash . Hashing.contentHash $
Hashing.Causal
{ Hashing.branchHash = unBranchHash branchHash,

View File

@ -4,6 +4,8 @@ module U.Codebase.Sqlite.V2.HashHandle
where
import Data.Set qualified as Set
import U.Codebase.Branch.Hashing qualified as H2
import U.Codebase.Causal.Hashing qualified as H2
import U.Codebase.Sqlite.HashHandle
import U.Util.Type (removeAllEffectVars)
import Unison.Hashing.V2 qualified as H2
@ -15,5 +17,7 @@ v2HashHandle =
{ toReference = h2ToV2Reference . H2.typeToReference . v2ToH2Type . removeAllEffectVars,
toReferenceMentions = Set.map h2ToV2Reference . H2.typeToReferenceMentions . v2ToH2Type . removeAllEffectVars,
toReferenceDecl = \h -> h2ToV2Reference . H2.typeToReference . v2ToH2TypeD h . removeAllEffectVars,
toReferenceDeclMentions = \h -> Set.map h2ToV2Reference . H2.typeToReferenceMentions . v2ToH2TypeD h . removeAllEffectVars
toReferenceDeclMentions = \h -> Set.map h2ToV2Reference . H2.typeToReferenceMentions . v2ToH2TypeD h . removeAllEffectVars,
hashBranch = H2.hashBranch,
hashCausal = H2.hashCausal
}

View File

@ -1,6 +1,6 @@
cabal-version: 1.12
-- This file has been generated from package.yaml by hpack version 0.35.1.
-- This file has been generated from package.yaml by hpack version 0.35.2.
--
-- see: https://github.com/sol/hpack
@ -18,9 +18,9 @@ source-repository head
library
exposed-modules:
U.Codebase.Sqlite.V2.HashHandle
other-modules:
U.Codebase.Branch.Hashing
U.Codebase.Causal.Hashing
other-modules:
Unison.Hashing.V2.Convert2
hs-source-dirs:
src

View File

@ -0,0 +1,31 @@
module U.Codebase.Causal.Squash (squashCausal) where
import U.Codebase.Branch.Type
import U.Codebase.Causal (Causal (..))
import U.Codebase.Sqlite.HashHandle qualified as HH
import U.Codebase.Sqlite.Operations qualified as SqliteOps
import Unison.Prelude
import Unison.Sqlite qualified as Sqlite
-- Recursively discards history, resulting in a namespace tree with only single a single
-- Causal node at every level.
squashCausal :: HH.HashHandle -> CausalBranch Sqlite.Transaction -> Sqlite.Transaction (CausalBranch Sqlite.Transaction)
squashCausal hashHandle@HH.HashHandle {hashCausal, hashBranch} Causal {valueHash = unsquashedBranchHash, value} = do
runMaybeT (MaybeT (SqliteOps.tryGetSquashResult unsquashedBranchHash) >>= MaybeT . SqliteOps.loadCausalBranchByCausalHash) >>= \case
Just cb -> pure cb
Nothing -> do
branch@Branch {children} <- value
squashedChildren <- traverse (squashCausal hashHandle) children
let squashedBranchHead = branch {children = squashedChildren}
squashedBranchHash <- hashBranch squashedBranchHead
let squashedCausalHash = hashCausal squashedBranchHash mempty
let squashedCausalBranch =
Causal
{ causalHash = squashedCausalHash,
valueHash = squashedBranchHash,
parents = mempty,
value = pure squashedBranchHead
}
SqliteOps.saveBranch hashHandle squashedCausalBranch
SqliteOps.saveSquashResult unsquashedBranchHash squashedCausalHash
pure squashedCausalBranch

View File

@ -3,6 +3,8 @@ module U.Codebase.Sqlite.HashHandle
)
where
import U.Codebase.Branch.Type (Branch)
import U.Codebase.HashTags
import U.Codebase.Reference qualified as C
import U.Codebase.Sqlite.Symbol (Symbol)
import U.Codebase.Term qualified as C.Term
@ -18,5 +20,12 @@ data HashHandle = HashHandle
-- | Hash the type of a single constructor in a decl component. The provided hash argument is the hash of the decl component.
toReferenceDecl :: Hash -> C.Type.TypeD Symbol -> C.Reference,
-- | Hash decl's mentions
toReferenceDeclMentions :: Hash -> C.Type.TypeD Symbol -> Set C.Reference
toReferenceDeclMentions :: Hash -> C.Type.TypeD Symbol -> Set C.Reference,
hashBranch :: forall m. Monad m => Branch m -> m BranchHash,
hashCausal ::
-- The causal's namespace hash
BranchHash ->
-- The causal's parents
Set CausalHash ->
CausalHash
}

View File

@ -14,6 +14,8 @@ module U.Codebase.Sqlite.Operations
expectBranchByBranchHashId,
expectNamespaceStatsByHash,
expectNamespaceStatsByHashId,
tryGetSquashResult,
saveSquashResult,
-- * terms
Q.saveTermComponent,
@ -1404,6 +1406,21 @@ deleteNameLookupsExceptFor reachable = do
bhIds <- for (Set.toList reachable) Q.expectBranchHashId
Q.deleteNameLookupsExceptFor bhIds
-- | Get the causal hash which would be the result of squashing the provided branch hash.
-- Returns Nothing if we haven't computed it before.
tryGetSquashResult :: BranchHash -> Transaction (Maybe CausalHash)
tryGetSquashResult bh = do
bhId <- Q.expectBranchHashId bh
chId <- Q.tryGetSquashResult bhId
traverse Q.expectCausalHash chId
-- | Saves the result of a squash
saveSquashResult :: BranchHash -> CausalHash -> Transaction ()
saveSquashResult bh ch = do
bhId <- Q.expectBranchHashId bh
chId <- Q.saveCausalHash ch
Q.saveSquashResult bhId chId
-- | Search for term or type names which contain the provided list of segments in order.
-- Search is case insensitive.
fuzzySearchDefinitions ::

View File

@ -75,6 +75,7 @@ module U.Codebase.Sqlite.Queries
-- ** causal table
saveCausal,
isCausalHash,
causalExistsByHash32,
expectCausal,
loadCausalHashIdByCausalHash,
expectCausalHashIdByCausalHash,
@ -85,6 +86,8 @@ module U.Codebase.Sqlite.Queries
loadBranchObjectIdByBranchHashId,
expectBranchObjectIdByCausalHashId,
expectBranchObjectIdByBranchHashId,
tryGetSquashResult,
saveSquashResult,
-- ** causal_parent table
saveCausalParents,
@ -233,6 +236,7 @@ module U.Codebase.Sqlite.Queries
fixScopedNameLookupTables,
addNameLookupMountTables,
addMostRecentNamespaceTable,
addSquashResultTable,
-- ** schema version
currentSchemaVersion,
@ -386,7 +390,7 @@ type TextPathSegments = [Text]
-- * main squeeze
currentSchemaVersion :: SchemaVersion
currentSchemaVersion = 13
currentSchemaVersion = 14
createSchema :: Transaction ()
createSchema = do
@ -439,6 +443,10 @@ addMostRecentNamespaceTable :: Transaction ()
addMostRecentNamespaceTable =
executeStatements (Text.pack [hereFile|unison/sql/008-add-most-recent-namespace-table.sql|])
addSquashResultTable :: Transaction ()
addSquashResultTable =
executeStatements (Text.pack [hereFile|unison/sql/009-add-squash-cache-table.sql|])
schemaVersion :: Transaction SchemaVersion
schemaVersion =
queryOneCol
@ -1215,6 +1223,19 @@ isCausalHash hash =
)
|]
-- | Return whether or not a causal exists with the given hash32.
causalExistsByHash32 :: Hash32 -> Transaction Bool
causalExistsByHash32 hash =
queryOneCol
[sql|
SELECT EXISTS (
SELECT 1
FROM causal
JOIN hash ON causal.self_hash_id = hash.id
WHERE hash.base32 = :hash
)
|]
loadBranchObjectIdByCausalHashId :: CausalHashId -> Transaction (Maybe BranchObjectId)
loadBranchObjectIdByCausalHashId id = queryMaybeCol (loadBranchObjectIdByCausalHashIdSql id)
@ -2179,7 +2200,7 @@ termNamesForRefWithinNamespace bhId namespaceRoot ref maySuffix = do
let suffixGlob = case maySuffix of
Just suffix -> toSuffixGlob suffix
Nothing -> "*"
queryListColCheck
directNames <- queryListColCheck
[sql|
SELECT reversed_name FROM scoped_term_name_lookup
WHERE root_branch_hash_id = :bhId
@ -2196,6 +2217,26 @@ termNamesForRefWithinNamespace bhId namespaceRoot ref maySuffix = do
AND reversed_name GLOB :suffixGlob
|]
\reversedNames -> for reversedNames reversedNameToReversedSegments
-- If we don't find a name in the name lookup, expand the search to recursively include transitive deps
-- and just return the first one we find.
if null directNames
then do
toList
<$> queryMaybeColCheck
[sql|
$transitive_dependency_mounts
SELECT (reversed_name || reversed_mount_path) AS reversed_name
FROM transitive_dependency_mounts
INNER JOIN scoped_term_name_lookup
ON scoped_term_name_lookup.root_branch_hash_id = transitive_dependency_mounts.root_branch_hash_id
WHERE referent_builtin IS @ref AND referent_component_hash IS @ AND referent_component_index IS @ AND referent_constructor_index IS @
AND reversed_name GLOB :suffixGlob
LIMIT 1
|]
(\reversedName -> reversedNameToReversedSegments reversedName)
else pure directNames
where
transitive_dependency_mounts = transitiveDependenciesSql bhId
-- | NOTE: requires that the codebase has an up-to-date name lookup index. As of writing, this
-- is only true on Share.
@ -2208,7 +2249,7 @@ typeNamesForRefWithinNamespace bhId namespaceRoot ref maySuffix = do
let suffixGlob = case maySuffix of
Just suffix -> toSuffixGlob suffix
Nothing -> "*"
queryListColCheck
directNames <- queryListColCheck
[sql|
SELECT reversed_name FROM scoped_type_name_lookup
WHERE root_branch_hash_id = :bhId
@ -2225,6 +2266,48 @@ typeNamesForRefWithinNamespace bhId namespaceRoot ref maySuffix = do
AND reversed_name GLOB :suffixGlob
|]
\reversedNames -> for reversedNames reversedNameToReversedSegments
-- If we don't find a name in the name lookup, expand the search to recursively include transitive deps
-- and just return the first one we find.
if null directNames
then
toList
<$> queryMaybeColCheck
[sql|
$transitive_dependency_mounts
SELECT (reversed_name || reversed_mount_path) AS reversed_name
FROM transitive_dependency_mounts
INNER JOIN scoped_type_name_lookup
ON scoped_type_name_lookup.root_branch_hash_id = transitive_dependency_mounts.root_branch_hash_id
WHERE reference_builtin IS @ref AND reference_component_hash IS @ AND reference_component_index IS @
AND reversed_name GLOB :suffixGlob
LIMIT 1
|]
(\reversedName -> reversedNameToReversedSegments reversedName)
else pure directNames
where
transitive_dependency_mounts = transitiveDependenciesSql bhId
-- | Brings into scope the transitive_dependency_mounts CTE table, which contains all transitive deps of the given root, but does NOT include the direct dependencies.
-- @transitive_dependency_mounts(root_branch_hash_id, reversed_mount_path)@
-- Where @reversed_mount_path@ is the reversed path from the provided root to the mounted
-- dependency's root.
transitiveDependenciesSql :: BranchHashId -> Sql
transitiveDependenciesSql rootBranchHashId =
[sql|
-- Recursive table containing all transitive deps
WITH RECURSIVE
transitive_dependency_mounts(root_branch_hash_id, reversed_mount_path) AS (
-- We've already searched direct deps above, so start with children of direct deps
SELECT transitive.mounted_root_branch_hash_id, transitive.reversed_mount_path || direct.reversed_mount_path
FROM name_lookup_mounts direct
JOIN name_lookup_mounts transitive on direct.mounted_root_branch_hash_id = transitive.parent_root_branch_hash_id
WHERE direct.parent_root_branch_hash_id = :rootBranchHashId
UNION ALL
SELECT mount.mounted_root_branch_hash_id, mount.reversed_mount_path || rec.reversed_mount_path
FROM name_lookup_mounts mount
INNER JOIN transitive_dependency_mounts rec ON mount.parent_root_branch_hash_id = rec.root_branch_hash_id
)
|]
-- | NOTE: requires that the codebase has an up-to-date name lookup index. As of writing, this
-- is only true on Share.
@ -3960,3 +4043,32 @@ setMostRecentNamespace namespace =
json :: Text
json =
Text.Lazy.toStrict (Aeson.encodeToLazyText namespace)
-- | Get the causal hash result from squashing the provided branch hash if we've squashed it
-- at some point in the past.
tryGetSquashResult :: BranchHashId -> Transaction (Maybe CausalHashId)
tryGetSquashResult bhId = do
queryMaybeCol
[sql|
SELECT
squashed_causal_hash_id
FROM
squash_results
WHERE
branch_hash_id = :bhId
|]
-- | Save the result of running a squash on the provided branch hash id.
saveSquashResult :: BranchHashId -> CausalHashId -> Transaction ()
saveSquashResult bhId chId =
execute
[sql|
INSERT INTO squash_results (
branch_hash_id,
squashed_causal_hash_id)
VALUES (
:bhId,
:chId
)
ON CONFLICT DO NOTHING
|]

View File

@ -0,0 +1,10 @@
-- A table for tracking the results of squashes we've performed.
-- This is used to avoid re-squashing the same branch multiple times.
CREATE TABLE "squash_results" (
-- The branch hash of the namespace to be squashed.
-- There should only ever be one result for each unsquashed value hash.
branch_hash_id INTEGER PRIMARY KEY NOT NULL REFERENCES hash(id),
-- The causal hash id which is the result of squashing the 'branch_hash_id's causal.'
squashed_causal_hash_id INTEGER NOT NULL REFERENCES causal(self_hash_id)
) WITHOUT ROWID;

View File

@ -18,6 +18,7 @@ extra-source-files:
sql/006-most-recent-branch-table.sql
sql/007-add-name-lookup-mounts.sql
sql/008-add-most-recent-namespace-table.sql
sql/009-add-squash-cache-table.sql
sql/create.sql
source-repository head
@ -27,6 +28,7 @@ source-repository head
library
exposed-modules:
U.Codebase.Branch
U.Codebase.Causal.Squash
U.Codebase.Sqlite.Branch.Diff
U.Codebase.Sqlite.Branch.Format
U.Codebase.Sqlite.Branch.Full

View File

@ -18,34 +18,7 @@ On startup, Unison prints a url for the codebase UI. If you did step 3 above, th
## Autoformatting your code with Ormolu
We use 0.5.0.1 of Ormolu and CI will fail if your code isn't properly formatted.
```
ghcup install ghc 9.2.7 # if not already installed
ghcup install cabal # if not already installed
cabal unpack ormolu-0.5.0.1
cd ormolu-0.5.0.1
cabal install -w ghc-9.2.7
```
You can then add the following to `.git/hooks/pre-commit` to make sure all your commits get formatted:
```
#!/bin/bash
set -e
if [[ -z "${SKIP_FORMATTING}" ]]; then
ormolu -i $(git diff --cached --name-only | grep '\.hs$')
git add $(git diff --cached --name-only)
fi
```
If you've got an existing PR that somehow hasn't been formatted correctly, you can install the correct version of Ormolu locally, then do:
```
ormolu -i $(git ls-files | grep '\.hs$')
```
We use 0.5.0.1 of Ormolu and CI will add an extra commit, if needed, to autoformat your code.
Also note that you can always wrap a comment around some code you don't want Ormolu to touch, using:

View File

@ -875,7 +875,8 @@ ioBuiltins =
( "IO.tryEval",
forall1 "a" $ \a ->
(unit --> io a) --> Type.effect () [Type.builtinIO (), DD.exceptionType ()] a
)
),
("IO.randomBytes", nat --> io bytes)
]
mvarBuiltins :: [(Text, Type)]

View File

@ -78,7 +78,8 @@ migrations getDeclType termBuffer declBuffer rootCodebasePath =
sqlMigration 10 Q.addProjectTables,
sqlMigration 11 Q.addMostRecentBranchTable,
(12, migrateSchema11To12),
sqlMigration 13 Q.addMostRecentNamespaceTable
sqlMigration 13 Q.addMostRecentNamespaceTable,
sqlMigration 14 Q.addSquashResultTable
]
where
sqlMigration :: SchemaVersion -> Sqlite.Transaction () -> (SchemaVersion, Sqlite.Transaction ())

View File

@ -1,7 +1,8 @@
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE UnicodeSyntax #-}
module Unison.FileParsers where
module Unison.FileParsers
( parseAndSynthesizeFile,
synthesizeFile',
)
where
import Control.Lens
import Control.Monad.State (evalStateT)
@ -50,8 +51,6 @@ type Type v = Type.Type v Ann
type UnisonFile v = UF.UnisonFile v Ann
type Result' v = Result (Seq (Note v Ann))
debug :: Bool
debug = False

View File

@ -37,6 +37,7 @@ import Control.Monad.Reader (ReaderT (..), ask, runReaderT)
import Control.Monad.State.Strict (State, execState, modify)
import Crypto.Hash qualified as Hash
import Crypto.MAC.HMAC qualified as HMAC
import Crypto.Random (getRandomBytes)
import Data.Bits (shiftL, shiftR, (.|.))
import Data.ByteArray qualified as BA
import Data.ByteString (hGet, hGetSome, hPut)
@ -2384,7 +2385,7 @@ declareForeigns = do
declareForeign Tracked "IO.listen.impl.v3" boxToEF0
. mkForeignIOF
$ \sk -> SYS.listenSock sk 2
$ \sk -> SYS.listenSock sk 2048
declareForeign Tracked "IO.clientSocket.impl.v3" boxBoxToEFBox
. mkForeignIOF
@ -2742,6 +2743,9 @@ declareForeigns = do
declareForeign Untracked "Universal.murmurHash" murmur'hash . mkForeign $
pure . asWord64 . hash64 . serializeValueLazy
declareForeign Tracked "IO.randomBytes" natToBox . mkForeign $
\n -> Bytes.fromArray <$> getRandomBytes @IO @ByteString n
declareForeign Untracked "Bytes.zlib.compress" boxDirect . mkForeign $ pure . Bytes.zlibCompress
declareForeign Untracked "Bytes.gzip.compress" boxDirect . mkForeign $ pure . Bytes.gzipCompress
declareForeign Untracked "Bytes.zlib.decompress" boxToEBoxBox . mkForeign $ \bs ->

View File

@ -1,6 +1,6 @@
cabal-version: 1.12
-- This file has been generated from package.yaml by hpack version 0.35.1.
-- This file has been generated from package.yaml by hpack version 0.35.2.
--
-- see: https://github.com/sol/hpack

View File

@ -3,7 +3,8 @@
racket/string
racket/file
rnrs/io/ports-6
(only-in racket empty?)
(only-in rnrs standard-error-port standard-input-port standard-output-port vector-map)
(only-in racket empty? with-output-to-string system/exit-code system false?)
compatibility/mlist
(only-in unison/boot data-case define-unison)
unison/data
@ -28,15 +29,21 @@
unison/concurrent
)
(provide
unison-FOp-IO.stdHandle
(prefix-out
builtin-IO.
(combine-out
seekHandle.impl.v3
getLine.impl.v1
getSomeBytes.impl.v1
getBuffering.impl.v3
setBuffering.impl.v3
getEcho.impl.v1
setEcho.impl.v1
getArgs.impl.v1
getEnv.impl.v1
process.call
))
; Still to implement:
@ -46,10 +53,6 @@
; ready.impl.v1
; isFileOpen.impl.v3
; isFileEOF.impl.v3
; setEcho.impl.v1
; getEcho.impl.v1
; - unsafe-port->file-descriptor
)
(define either-id (bytevector 6 15 103 128 65 126 44 164 169 154 106 164 187 86 33 156 155 89 79 64 71 158 119 151 142 79 121 206 247 92 41 13 151 250 243 205 13 193 134 218 198 145 193 96 55 87 92 215 34 52 161 162 226 22 169 43 228 184 86 77 149 58 66 125))
@ -83,20 +86,31 @@
(Right (string->chunked-string line))
)))
(define-unison (getSomeBytes.impl.v1 handle bytes)
(let* ([buffer (make-bytes bytes)]
[line (read-bytes-avail! buffer handle)])
(if (eof-object? line)
(Right (bytes->chunked-bytes #""))
(Right (bytes->chunked-bytes buffer))
)))
(define BufferMode
(data 'Reference 1 (data 'Id 0 (bytevector 107 13 114 185 126 64 211 42 13 102 196 109 125 88 217 3 36 251 159 9 35 172 24 16 54 158 72 167 2 22 248 214 77 251 43 81 18 154 173 92 126 242 69 233 142 79 137 22 152 161 71 175 85 193 31 162 82 54 3 70 220 161 142 37) 0)))
(define BlockBuffering (data BufferMode 2))
(define LineBuffering (data BufferMode 1))
(define NoBuffering (data BufferMode 0))
(define Boolean (data 'Reference 0 (string->chunked-string "Boolean")))
(define True (data Boolean 1))
(define False (data Boolean 0))
(define-unison (getBuffering.impl.v3 handle)
(case (file-stream-buffer-mode handle)
[(none) (Right NoBuffering)]
[(line) (Right LineBuffering)]
[(block) (Right BlockBuffering)]
[(#f) (Exception 'IO "Unable to determine buffering mode of handle")]
[else (Exception 'IO "Unexpected response from file-stream-buffer-mode")]))
[(#f) (Exception 'IO "Unable to determine buffering mode of handle" '())]
[else (Exception 'IO "Unexpected response from file-stream-buffer-mode" '())]))
(define-unison (setBuffering.impl.v3 handle mode)
(data-case mode
@ -110,5 +124,65 @@
(file-stream-buffer-mode handle 'block)
(Right none))
(3 (size)
(Exception 'IO "Sized block buffering not supported"))))
(Exception 'IO "Sized block buffering not supported" '()))))
(define (with-buffer-mode port mode)
(file-stream-buffer-mode port mode)
port)
(define stdin (with-buffer-mode (standard-input-port) 'none))
(define stdout (with-buffer-mode (standard-output-port) 'line))
(define stderr (with-buffer-mode (standard-error-port) 'line))
(define (unison-FOp-IO.stdHandle n)
(case n
[(0) stdin]
[(1) stdout]
[(2) stderr]))
(define-unison (getEcho.impl.v1 handle)
(if (eq? handle stdin)
(Right (if (get-stdin-echo) True False))
(Exception 'IO "getEcho only supported on stdin" '())))
(define-unison (setEcho.impl.v1 handle echo)
(if (eq? handle stdin)
(begin
(data-case echo
(1 () (system "stty echo"))
(0 () (system "stty -echo")))
(Right none))
(Exception 'IO "setEcho only supported on stdin" '())))
(define (get-stdin-echo)
(let ([current (with-output-to-string (lambda () (system "stty -a")))])
(string-contains? current " echo ")))
(define-unison (getArgs.impl.v1 unit)
(Right (vector->chunked-list
(vector-map string->chunked-string (current-command-line-arguments)))))
(define-unison (getEnv.impl.v1 key)
(let ([value (environment-variables-ref (current-environment-variables) (string->bytes/utf-8 (chunked-string->string key)))])
(if (false? value)
(Exception 'IO "environmental variable not found" key)
(Right (string->chunked-string (bytes->string/utf-8 value))))))
;; From https://github.com/sorawee/shlex/blob/5de06500e8c831cfc8dffb99d57a76decc02c569/main.rkt (MIT License)
;; with is a port of https://github.com/python/cpython/blob/bf2f76ec0976c09de79c8827764f30e3b6fba776/Lib/shlex.py#L325
(define unsafe-pattern #rx"[^a-zA-Z0-9_@%+=:,./-]")
(define (quote-arg s)
(if (non-empty-string? s)
(if (regexp-match unsafe-pattern s)
(string-append "'" (string-replace s "'" "'\"'\"'") "'")
s)
"''"))
(define-unison (process.call command arguments)
(system/exit-code
(string-join (cons
(chunked-string->string command)
(map (lambda (arg) (quote-arg (chunked-string->string arg)))
(vector->list
(chunked-list->vector arguments))))
" ")))

View File

@ -36,6 +36,8 @@
builtin-Int.signum
builtin-Nat.increment
builtin-Nat.toFloat
builtin-Text.indexOf
builtin-IO.randomBytes
unison-FOp-internal.dataTag
unison-FOp-Char.toText
@ -48,8 +50,14 @@
unison-FOp-IO.getBytes.impl.v3
builtin-IO.seekHandle.impl.v3
builtin-IO.getLine.impl.v1
builtin-IO.getSomeBytes.impl.v1
builtin-IO.setBuffering.impl.v3
builtin-IO.getBuffering.impl.v3
builtin-IO.setEcho.impl.v1
builtin-IO.process.call
builtin-IO.getEcho.impl.v1
builtin-IO.getArgs.impl.v1
builtin-IO.getEnv.impl.v1
unison-FOp-IO.getFileSize.impl.v3
unison-FOp-IO.getFileTimestamp.impl.v3
unison-FOp-IO.fileExists.impl.v3
@ -350,6 +358,8 @@
exn:fail:contract?
file-stream-buffer-mode
with-handlers
match
regexp-match-positions
sequence-ref
vector-copy!
bytes-copy!)
@ -357,6 +367,7 @@
(unison arithmetic)
(unison bytevector)
(unison core)
(only (unison boot) define-unison)
(unison data)
(unison math)
(unison chunked-seq)
@ -371,7 +382,21 @@
(unison tcp)
(unison gzip)
(unison zlib)
(unison concurrent))
(unison concurrent)
(racket random))
; NOTE: this is just a temporary stopgap until the real function is
; done. I accidentally pulled in too new a version of base in the
; project version of the unison compiler and it broke the jit tests.
(define-unison (builtin-Text.indexOf s t)
(let ([ss (chunked-string->string s)]
[tt (chunked-string->string t)])
(match (regexp-match-positions ss tt)
[#f (data 'Optional 1)] ; none
[(cons (cons i j) r) (data 'Optional 0 i)]))) ; some
(define-unison (builtin-IO.randomBytes n)
(bytes->chunked-bytes (crypto-random-bytes n)))
(define (unison-POp-UPKB bs)
(build-chunked-list
@ -579,20 +604,6 @@
(define (unison-FOp-Char.toText c) (string->chunked-string (string (integer->char c))))
(define (with-buffer-mode port mode)
(file-stream-buffer-mode port mode)
port)
(define stdin (with-buffer-mode (standard-input-port) 'none))
(define stdout (with-buffer-mode (standard-output-port) 'line))
(define stderr (with-buffer-mode (standard-error-port) 'line))
(define (unison-FOp-IO.stdHandle n)
(case n
[(0) stdin]
[(1) stdout]
[(2) stderr]))
(define (unison-FOp-IO.getArgs.impl.v1)
(sum 1 (cdr (command-line))))

View File

@ -56,11 +56,11 @@ test = do
expectExitCode ExitSuccess ucm ["transcript.fork", transcriptFile, "--codebase-create", tempCodebase] "",
-- , expectExitCode ExitSuccess ucm ["headless"] "" -- ?
-- options
expectExitCode ExitSuccess ucm ["--port", "8000", "--codebase-create", tempCodebase, "--no-base"] "exit",
expectExitCode ExitSuccess ucm ["--host", "localhost", "--codebase-create", tempCodebase, "--no-base"] "exit",
expectExitCode ExitSuccess ucm ["--token", "MY_TOKEN", "--codebase-create", tempCodebase, "--no-base"] "exit", -- ?
expectExitCode ExitSuccess ucm ["--codebase-create", tempCodebase, "--no-base"] "exit",
expectExitCode ExitSuccess ucm ["--ui", tempCodebase, "--codebase-create", tempCodebase, "--no-base"] "exit",
expectExitCode ExitSuccess ucm ["--port", "8000", "--codebase-create", tempCodebase] "exit",
expectExitCode ExitSuccess ucm ["--host", "localhost", "--codebase-create", tempCodebase] "exit",
expectExitCode ExitSuccess ucm ["--token", "MY_TOKEN", "--codebase-create", tempCodebase] "exit", -- ?
expectExitCode ExitSuccess ucm ["--codebase-create", tempCodebase] "exit",
expectExitCode ExitSuccess ucm ["--ui", tempCodebase, "--codebase-create", tempCodebase] "exit",
scope "can compile, then run compiled artifact" $
tests
[ expectExitCode ExitSuccess ucm ["transcript", transcriptFile] "",
@ -78,7 +78,7 @@ expectExitCode expected cmd args stdin = scope (intercalate " " (cmd : args)) do
expectEqual code expected
defaultArgs :: [String]
defaultArgs = ["--codebase-create", tempCodebase, "--no-base"]
defaultArgs = ["--codebase-create", tempCodebase]
clearTempCodebase :: () -> IO ()
clearTempCodebase _ =

View File

@ -189,6 +189,7 @@ default-extensions:
- InstanceSigs
- LambdaCase
- MultiParamTypeClasses
- MultiWayIf
- NamedFieldPuns
- NumericUnderscores
- OverloadedLabels

View File

@ -0,0 +1,35 @@
-- | servant-client utilities
module Unison.Cli.ServantClientUtils
( ConnectionError (..),
classifyConnectionError,
)
where
import Control.Exception (fromException)
import Network.HTTP.Client qualified as HttpClient
import System.IO.Error (isDoesNotExistError)
import Unison.Prelude
data ConnectionError
= ConnectionError'Offline
| ConnectionError'SomethingElse HttpClient.HttpExceptionContent
| ConnectionError'SomethingEntirelyUnexpected SomeException
-- | Given a 'SomeException' from a @servant-client@ 'ClientError', attempt to classify what happened.
classifyConnectionError :: SomeException -> ConnectionError
classifyConnectionError exception0 =
case fromException exception0 of
Just (HttpClient.HttpExceptionRequest _request content) ->
fromMaybe (ConnectionError'SomethingElse content) do
case content of
HttpClient.ConnectionFailure exception1 -> do
ioException <- fromException @IOException exception1
if
| -- This may not be 100% accurate... but if the initial `getAddrInfo` request fails it will indeed throw
-- a "does not exist" error. It seems in order to *know* that `getAddrInfo` was the cause of this
-- exception, we'd have to parse the `show` output, which is preposterous.
isDoesNotExistError ioException ->
Just ConnectionError'Offline
| otherwise -> Nothing
_ -> Nothing
_ -> ConnectionError'SomethingEntirelyUnexpected exception0

View File

@ -14,10 +14,12 @@ module Unison.Cli.Share.Projects
-- * API functions
getProjectById,
getProjectByName,
getProjectByName',
createProject,
GetProjectBranchResponse (..),
getProjectBranchById,
getProjectBranchByName,
getProjectBranchByName',
createProjectBranch,
SetProjectBranchHeadResponse (..),
setProjectBranchHead,
@ -35,6 +37,7 @@ import Network.URI (URI)
import Network.URI qualified as URI
import Servant.API ((:<|>) (..), (:>))
import Servant.Client
import Servant.Client qualified as Servant
import U.Codebase.Sqlite.DbId (RemoteProjectBranchId (..), RemoteProjectId (..))
import U.Codebase.Sqlite.Queries qualified as Queries
import Unison.Auth.HTTPClient qualified as Auth
@ -54,16 +57,22 @@ import Unison.Share.Types (codeserverBaseURL)
-- On success, update the `remote_project` table.
getProjectById :: RemoteProjectId -> Cli (Maybe RemoteProject)
getProjectById (RemoteProjectId projectId) = do
response <- servantClientToCli (getProject0 (Just projectId) Nothing)
response <- servantClientToCli (getProject0 (Just projectId) Nothing) & onLeftM servantClientError
onGetProjectResponse response
-- | Get a project by name.
--
-- On success, update the `remote_project` table.
getProjectByName :: ProjectName -> Cli (Maybe RemoteProject)
getProjectByName projectName = do
response <- servantClientToCli (getProject0 Nothing (Just (into @Text projectName)))
onGetProjectResponse response
getProjectByName projectName =
getProjectByName' projectName & onLeftM servantClientError
-- | Variant of 'getProjectByName' that returns servant client errors.
getProjectByName' :: ProjectName -> Cli (Either Servant.ClientError (Maybe RemoteProject))
getProjectByName' projectName = do
servantClientToCli (getProject0 Nothing (Just (into @Text projectName))) >>= \case
Left err -> pure (Left err)
Right response -> Right <$> onGetProjectResponse response
-- | Create a new project. Kinda weird: returns `Nothing` if the user handle part of the project doesn't exist.
--
@ -72,9 +81,10 @@ createProject :: ProjectName -> Cli (Maybe RemoteProject)
createProject projectName = do
let request = Share.API.CreateProjectRequest {projectName = into @Text projectName}
servantClientToCli (createProject0 request) >>= \case
Share.API.CreateProjectResponseNotFound {} -> pure Nothing
Share.API.CreateProjectResponseUnauthorized x -> unauthorized x
Share.API.CreateProjectResponseSuccess project -> Just <$> onGotProject project
Left err -> servantClientError err
Right (Share.API.CreateProjectResponseNotFound {}) -> pure Nothing
Right (Share.API.CreateProjectResponseUnauthorized x) -> unauthorized x
Right (Share.API.CreateProjectResponseSuccess project) -> Just <$> onGotProject project
data GetProjectBranchResponse
= GetProjectBranchResponseBranchNotFound
@ -86,7 +96,7 @@ data GetProjectBranchResponse
-- On success, update the `remote_project_branch` table.
getProjectBranchById :: ProjectAndBranch RemoteProjectId RemoteProjectBranchId -> Cli GetProjectBranchResponse
getProjectBranchById (ProjectAndBranch (RemoteProjectId projectId) (RemoteProjectBranchId branchId)) = do
response <- servantClientToCli (getProjectBranch0 projectId (Just branchId) Nothing)
response <- servantClientToCli (getProjectBranch0 projectId (Just branchId) Nothing) & onLeftM servantClientError
onGetProjectBranchResponse response
-- | Get a project branch by name.
@ -94,19 +104,31 @@ getProjectBranchById (ProjectAndBranch (RemoteProjectId projectId) (RemoteProjec
-- On success, update the `remote_project_branch` table.
getProjectBranchByName :: ProjectAndBranch RemoteProjectId ProjectBranchName -> Cli GetProjectBranchResponse
getProjectBranchByName (ProjectAndBranch (RemoteProjectId projectId) branchName) = do
response <- servantClientToCli (getProjectBranch0 projectId Nothing (Just (into @Text branchName)))
response <-
servantClientToCli (getProjectBranch0 projectId Nothing (Just (into @Text branchName)))
& onLeftM servantClientError
onGetProjectBranchResponse response
-- | Variant of 'getProjectBranchByName' that returns servant client errors.
getProjectBranchByName' ::
ProjectAndBranch RemoteProjectId ProjectBranchName ->
Cli (Either Servant.ClientError GetProjectBranchResponse)
getProjectBranchByName' (ProjectAndBranch (RemoteProjectId projectId) branchName) = do
servantClientToCli (getProjectBranch0 projectId Nothing (Just (into @Text branchName))) >>= \case
Left err -> pure (Left err)
Right response -> Right <$> onGetProjectBranchResponse response
-- | Create a new project branch.
--
-- On success, update the `remote_project_branch` table.
createProjectBranch :: Share.API.CreateProjectBranchRequest -> Cli (Maybe RemoteProjectBranch)
createProjectBranch request =
servantClientToCli (createProjectBranch0 request) >>= \case
Share.API.CreateProjectBranchResponseMissingCausalHash hash -> bugRemoteMissingCausalHash hash
Share.API.CreateProjectBranchResponseNotFound {} -> pure Nothing
Share.API.CreateProjectBranchResponseUnauthorized x -> unauthorized x
Share.API.CreateProjectBranchResponseSuccess branch -> Just <$> onGotProjectBranch branch
Left err -> servantClientError err
Right (Share.API.CreateProjectBranchResponseMissingCausalHash hash) -> bugRemoteMissingCausalHash hash
Right (Share.API.CreateProjectBranchResponseNotFound {}) -> pure Nothing
Right (Share.API.CreateProjectBranchResponseUnauthorized x) -> unauthorized x
Right (Share.API.CreateProjectBranchResponseSuccess branch) -> Just <$> onGotProjectBranch branch
data SetProjectBranchHeadResponse
= SetProjectBranchHeadResponseNotFound
@ -121,14 +143,15 @@ data SetProjectBranchHeadResponse
setProjectBranchHead :: Share.API.SetProjectBranchHeadRequest -> Cli SetProjectBranchHeadResponse
setProjectBranchHead request =
servantClientToCli (setProjectBranchHead0 request) >>= \case
Share.API.SetProjectBranchHeadResponseUnauthorized x -> unauthorized x
Share.API.SetProjectBranchHeadResponseNotFound _ -> pure SetProjectBranchHeadResponseNotFound
Share.API.SetProjectBranchHeadResponseMissingCausalHash hash -> bugRemoteMissingCausalHash hash
Share.API.SetProjectBranchHeadResponseExpectedCausalHashMismatch expected actual ->
Left err -> servantClientError err
Right (Share.API.SetProjectBranchHeadResponseUnauthorized x) -> unauthorized x
Right (Share.API.SetProjectBranchHeadResponseNotFound _) -> pure SetProjectBranchHeadResponseNotFound
Right (Share.API.SetProjectBranchHeadResponseMissingCausalHash hash) -> bugRemoteMissingCausalHash hash
Right (Share.API.SetProjectBranchHeadResponseExpectedCausalHashMismatch expected actual) ->
pure (SetProjectBranchHeadResponseExpectedCausalHashMismatch expected actual)
Share.API.SetProjectBranchHeadResponsePublishedReleaseIsImmutable -> pure SetProjectBranchHeadResponsePublishedReleaseIsImmutable
Share.API.SetProjectBranchHeadResponseDeprecatedReleaseIsImmutable -> pure SetProjectBranchHeadResponseDeprecatedReleaseIsImmutable
Share.API.SetProjectBranchHeadResponseSuccess -> pure SetProjectBranchHeadResponseSuccess
Right (Share.API.SetProjectBranchHeadResponsePublishedReleaseIsImmutable) -> pure SetProjectBranchHeadResponsePublishedReleaseIsImmutable
Right (Share.API.SetProjectBranchHeadResponseDeprecatedReleaseIsImmutable) -> pure SetProjectBranchHeadResponseDeprecatedReleaseIsImmutable
Right (Share.API.SetProjectBranchHeadResponseSuccess) -> pure SetProjectBranchHeadResponseSuccess
------------------------------------------------------------------------------------------------------------------------
-- Database manipulation callbacks
@ -152,8 +175,9 @@ onGotProject :: Share.API.Project -> Cli RemoteProject
onGotProject project = do
let projectId = RemoteProjectId (project ^. #projectId)
projectName <- validateProjectName (project ^. #projectName)
let latestRelease = (project ^. #latestRelease) >>= eitherToMaybe . tryFrom @Text
Cli.runTransaction (Queries.ensureRemoteProject projectId hardCodedUri projectName)
pure RemoteProject {projectId, projectName}
pure RemoteProject {projectId, projectName, latestRelease}
onGotProjectBranch :: Share.API.ProjectBranch -> Cli RemoteProjectBranch
onGotProjectBranch branch = do
@ -186,6 +210,10 @@ validateBranchName branchName =
tryInto @ProjectBranchName branchName & onLeft \_ ->
Cli.returnEarly (Output.InvalidProjectBranchName branchName)
servantClientError :: Servant.ClientError -> Cli void
servantClientError =
Cli.returnEarly . Output.ServantClientError
unauthorized :: Share.API.Unauthorized -> Cli void
unauthorized (Share.API.Unauthorized message) =
Cli.returnEarly (Output.Unauthorized message)
@ -210,7 +238,7 @@ hardCodedUri =
Nothing -> error ("BaseUrl is an invalid URI: " ++ showBaseUrl hardCodedBaseUrl)
Just uri -> uri
servantClientToCli :: ClientM a -> Cli a
servantClientToCli :: ClientM a -> Cli (Either Servant.ClientError a)
servantClientToCli action = do
Cli.Env {authHTTPClient = Auth.AuthenticatedHttpClient httpManager} <- ask
@ -218,8 +246,7 @@ servantClientToCli action = do
clientEnv =
mkClientEnv httpManager hardCodedBaseUrl
liftIO (runClientM action clientEnv) & onLeftM \err ->
Cli.returnEarly (Output.ServantClientError err)
liftIO (runClientM action clientEnv)
getProject0 :: Maybe Text -> Maybe Text -> ClientM Share.API.GetProjectResponse
createProject0 :: Share.API.CreateProjectRequest -> ClientM Share.API.CreateProjectResponse

View File

@ -9,13 +9,14 @@ where
import U.Codebase.Sqlite.DbId (RemoteProjectBranchId (..), RemoteProjectId (..))
import Unison.Prelude
import Unison.Project (ProjectBranchName, ProjectName)
import Unison.Project (ProjectBranchName, ProjectName, Semver)
import Unison.Share.API.Hash qualified as Share.API
-- | A remote project.
data RemoteProject = RemoteProject
{ projectId :: RemoteProjectId,
projectName :: ProjectName
projectName :: ProjectName,
latestRelease :: Maybe Semver
}
deriving stock (Eq, Generic, Show)

View File

@ -2,6 +2,7 @@ module Unison.Cli.TypeCheck
( typecheck,
typecheckHelper,
typecheckFile,
typecheckFile',
typecheckTerm,
)
where

View File

@ -106,7 +106,7 @@ import Unison.Codebase.Editor.Input qualified as Input
import Unison.Codebase.Editor.Output
import Unison.Codebase.Editor.Output qualified as Output
import Unison.Codebase.Editor.Output.DumpNamespace qualified as Output.DN
import Unison.Codebase.Editor.RemoteRepo (ReadRemoteNamespace (..), ReadShareLooseCode (..), ShareUserHandle (..))
import Unison.Codebase.Editor.RemoteRepo (ReadRemoteNamespace (..))
import Unison.Codebase.Editor.RemoteRepo qualified as RemoteRepo
import Unison.Codebase.Editor.Slurp qualified as Slurp
import Unison.Codebase.Editor.SlurpResult qualified as SlurpResult
@ -144,6 +144,7 @@ import Unison.HashQualified qualified as HQ
import Unison.HashQualified' qualified as HQ'
import Unison.HashQualified' qualified as HashQualified
import Unison.Hashing.V2.Convert qualified as Hashing
import Unison.JitInfo qualified as JitInfo
import Unison.LabeledDependency (LabeledDependency)
import Unison.LabeledDependency qualified as LD
import Unison.LabeledDependency qualified as LabeledDependency
@ -215,6 +216,7 @@ import Unison.Var (Var)
import Unison.Var qualified as Var
import Unison.WatchKind qualified as WK
import Web.Browser (openBrowser)
import Witch (unsafeFrom)
------------------------------------------------------------------------------------------------------------------------
-- Main loop
@ -1291,7 +1293,8 @@ loop e = do
CompileSchemeI output main -> doCompileScheme output main
ExecuteSchemeI main args -> doRunAsScheme main args
GenSchemeLibsI -> doGenerateSchemeBoot True Nothing
FetchSchemeCompilerI name -> doFetchCompiler name
FetchSchemeCompilerI name branch ->
doFetchCompiler name branch
IOTestI main -> handleIOTest main
-- UpdateBuiltinsI -> do
-- stepAt updateBuiltins
@ -1357,7 +1360,7 @@ loop e = do
externalDependencies <-
Cli.runTransaction (NamespaceDependencies.namespaceDependencies codebase (Branch.head b))
ppe <- PPE.unsuffixifiedPPE <$> currentPrettyPrintEnvDecl Backend.Within
Cli.respond $ ListNamespaceDependencies ppe path externalDependencies
Cli.respondNumbered $ ListNamespaceDependencies ppe path externalDependencies
DebugNumberedArgsI -> do
numArgs <- use #numberedArgs
Cli.respond (DumpNumberedArgs numArgs)
@ -1472,7 +1475,7 @@ loop e = do
handleDiffNamespaceToPatch description diffNamespaceToPatchInput
ProjectRenameI name -> handleProjectRename name
ProjectSwitchI name -> projectSwitch name
ProjectCreateI name -> projectCreate name
ProjectCreateI tryDownloadingBase name -> projectCreate tryDownloadingBase name
ProjectsI -> handleProjects
BranchI source name -> handleBranch source name
BranchRenameI name -> handleBranchRename name
@ -1624,7 +1627,8 @@ inputDescription input =
<> Text.unwords (fmap Text.pack args)
CompileSchemeI fi nm -> pure ("compile.native " <> HQ.toText nm <> " " <> Text.pack fi)
GenSchemeLibsI -> pure "compile.native.genlibs"
FetchSchemeCompilerI name -> pure ("compile.native.fetch" <> Text.pack name)
FetchSchemeCompilerI name branch ->
pure ("compile.native.fetch" <> Text.pack name <> " " <> Text.pack branch)
CreateAuthorI (NameSegment id) name -> pure ("create.author " <> id <> " " <> name)
RemoveTermReplacementI src p0 -> do
p <- opatch p0
@ -2436,24 +2440,25 @@ compilerPath = Path.Path' {Path.unPath' = Left abs}
rootPath = Path.Path {Path.toSeq = Seq.fromList segs}
abs = Path.Absolute {Path.unabsolute = rootPath}
doFetchCompiler :: String -> Cli ()
doFetchCompiler username =
doFetchCompiler :: String -> String -> Cli ()
doFetchCompiler username branch =
doPullRemoteBranch sourceTarget SyncMode.Complete Input.PullWithoutHistory Verbosity.Silent
where
-- fetching info
ns =
ReadShareLooseCode
{ server = RemoteRepo.DefaultCodeserver,
repo = ShareUserHandle (Text.pack username),
path =
Path.fromList $ NameSegment <$> ["public", "internal", "trunk"]
}
sourceTarget = PullSourceTarget2 (ReadShare'LooseCode ns) (This compilerPath)
prj =
These
(unsafeFrom $ "@" <> Text.pack username <> "/internal")
(unsafeFrom $ Text.pack branch)
sourceTarget =
PullSourceTarget2
(ReadShare'ProjectBranch prj)
(This compilerPath)
ensureCompilerExists :: Cli ()
ensureCompilerExists =
Cli.branchExistsAtPath' compilerPath
>>= flip unless (doFetchCompiler "unison")
>>= flip unless (doFetchCompiler "unison" JitInfo.currentRelease)
getCacheDir :: Cli String
getCacheDir = liftIO $ getXdgDirectory XdgCache "unisonlanguage"

View File

@ -4,6 +4,9 @@ module Unison.Codebase.Editor.HandleInput.ProjectCreate
)
where
import Control.Lens (over, (^.))
import Control.Monad.Reader (ask)
import Data.Map.Strict qualified as Map
import Data.Text qualified as Text
import Data.UUID.V4 qualified as UUID
import System.Random.Shuffle qualified as RandomShuffle
@ -14,12 +17,19 @@ import Unison.Cli.Monad (Cli)
import Unison.Cli.Monad qualified as Cli
import Unison.Cli.MonadUtils qualified as Cli (stepAt)
import Unison.Cli.ProjectUtils (projectBranchPath)
import Unison.Cli.Share.Projects qualified as Share
import Unison.Codebase qualified as Codebase
import Unison.Codebase.Branch qualified as Branch
import Unison.Codebase.Editor.HandleInput.Pull qualified as Pull
import Unison.Codebase.Editor.Output qualified as Output
import Unison.Codebase.Path qualified as Path
import Unison.Name qualified as Name
import Unison.NameSegment (NameSegment (NameSegment))
import Unison.Prelude
import Unison.Project (ProjectAndBranch (..), ProjectBranchName, ProjectName)
import Unison.Share.API.Hash qualified as Share.API
import Unison.Sqlite qualified as Sqlite
import Unison.Sync.Common qualified as Sync.Common
import Witch (unsafeFrom)
-- | Create a new project.
@ -48,8 +58,8 @@ import Witch (unsafeFrom)
--
-- For now, it doesn't seem worth it to do (1) or (2), since we want to do (3) eventually, and we'd rather not waste too
-- much time getting everything perfectly correct before we get there.
projectCreate :: Maybe ProjectName -> Cli ()
projectCreate maybeProjectName = do
projectCreate :: Bool -> Maybe ProjectName -> Cli ()
projectCreate tryDownloadingBase maybeProjectName = do
projectId <- liftIO (ProjectId <$> UUID.nextRandom)
branchId <- liftIO (ProjectBranchId <$> UUID.nextRandom)
@ -78,10 +88,71 @@ projectCreate maybeProjectName = do
True -> pure (Left (Output.ProjectNameAlreadyExists projectName))
let path = projectBranchPath ProjectAndBranch {project = projectId, branch = branchId}
Cli.stepAt "project.create" (Path.unabsolute path, const Branch.empty0)
Cli.respond (Output.CreatedProject (isNothing maybeProjectName) projectName)
Cli.cd path
maybeBaseLatestReleaseBranchObject <-
if tryDownloadingBase
then do
Cli.respond Output.FetchingLatestReleaseOfBase
-- Make an effort to pull the latest release of base, which can go wrong in a number of ways, the most likely of
-- which is that the user is offline.
maybeBaseLatestReleaseBranchObject <-
Cli.label \done -> do
baseProject <-
Share.getProjectByName' (unsafeFrom @Text "@unison/base") >>= \case
Right (Just baseProject) -> pure baseProject
_ -> done Nothing
ver <- baseProject ^. #latestRelease & onNothing (done Nothing)
let baseProjectId = baseProject ^. #projectId
let baseLatestReleaseBranchName = unsafeFrom @Text ("releases/" <> into @Text ver)
response <-
Share.getProjectBranchByName' (ProjectAndBranch baseProjectId baseLatestReleaseBranchName)
& onLeftM \_err -> done Nothing
baseLatestReleaseBranch <-
case response of
Share.GetProjectBranchResponseBranchNotFound -> done Nothing
Share.GetProjectBranchResponseProjectNotFound -> done Nothing
Share.GetProjectBranchResponseSuccess branch -> pure branch
Pull.downloadShareProjectBranch baseLatestReleaseBranch
Cli.Env {codebase} <- ask
baseLatestReleaseBranchObject <-
liftIO $
Codebase.expectBranchForHash
codebase
(Sync.Common.hash32ToCausalHash (Share.API.hashJWTHash (baseLatestReleaseBranch ^. #branchHead)))
pure (Just baseLatestReleaseBranchObject)
when (isNothing maybeBaseLatestReleaseBranchObject) do
Cli.respond Output.FailedToFetchLatestReleaseOfBase
pure maybeBaseLatestReleaseBranchObject
else pure Nothing
let projectBranchObject =
case maybeBaseLatestReleaseBranchObject of
Nothing -> Branch.empty0
Just baseLatestReleaseBranchObject ->
let -- lib.base
projectBranchLibBaseObject =
over
Branch.children
(Map.insert (NameSegment "base") baseLatestReleaseBranchObject)
Branch.empty0
projectBranchLibObject = Branch.cons projectBranchLibBaseObject Branch.empty
in over
Branch.children
(Map.insert Name.libSegment projectBranchLibObject)
Branch.empty0
Cli.stepAt reflogDescription (Path.unabsolute path, const projectBranchObject)
Cli.respond Output.HappyCoding
where
reflogDescription =
case maybeProjectName of
Nothing -> "project.create"
Just projectName -> "project.create " <> into @Text projectName
insertProjectAndBranch :: ProjectId -> ProjectName -> ProjectBranchId -> ProjectBranchName -> Sqlite.Transaction ()
insertProjectAndBranch projectId projectName branchId branchName = do
Queries.insertProject projectId projectName

View File

@ -5,6 +5,7 @@ module Unison.Codebase.Editor.HandleInput.Pull
loadPropagateDiffDefaultPatch,
mergeBranchAndPropagateDefaultPatch,
propagatePatch,
downloadShareProjectBranch,
withEntitiesDownloadedProgressCallback,
)
where
@ -180,23 +181,28 @@ loadRemoteNamespaceIntoMemory syncMode pullMode remoteNamespace = do
Cli.returnEarly (Output.GitError err)
ReadShare'LooseCode repo -> loadShareLooseCodeIntoMemory repo
ReadShare'ProjectBranch remoteBranch -> do
let repoInfo = Share.RepoInfo (into @Text (ProjectAndBranch (remoteBranch ^. #projectName) remoteProjectBranchName))
causalHash = Common.hash32ToCausalHash . Share.hashJWTHash $ causalHashJwt
causalHashJwt = remoteBranch ^. #branchHead
remoteProjectBranchName = remoteBranch ^. #branchName
(result, numDownloaded) <-
Cli.with withEntitiesDownloadedProgressCallback \(downloadedCallback, getNumDownloaded) -> do
result <- Share.downloadEntities Share.hardCodedBaseUrl repoInfo causalHashJwt downloadedCallback
numDownloaded <- liftIO getNumDownloaded
pure (result, numDownloaded)
case result of
Left err0 ->
(Cli.returnEarly . Output.ShareError) case err0 of
Share.SyncError err -> Output.ShareErrorDownloadEntities err
Share.TransportError err -> Output.ShareErrorTransport err
Right () -> do
Cli.respond (Output.DownloadedEntities numDownloaded)
liftIO (Codebase.expectBranchForHash codebase causalHash)
downloadShareProjectBranch remoteBranch
let causalHash = Common.hash32ToCausalHash (Share.hashJWTHash (remoteBranch ^. #branchHead))
liftIO (Codebase.expectBranchForHash codebase causalHash)
-- | @downloadShareProjectBranch branch@ downloads the given branch.
downloadShareProjectBranch :: Share.RemoteProjectBranch -> Cli ()
downloadShareProjectBranch branch = do
let repoInfo = Share.RepoInfo (into @Text (ProjectAndBranch (branch ^. #projectName) remoteProjectBranchName))
causalHashJwt = branch ^. #branchHead
remoteProjectBranchName = branch ^. #branchName
exists <- Cli.runTransaction (Queries.causalExistsByHash32 (Share.hashJWTHash causalHashJwt))
when (not exists) do
(result, numDownloaded) <-
Cli.with withEntitiesDownloadedProgressCallback \(downloadedCallback, getNumDownloaded) -> do
result <- Share.downloadEntities Share.hardCodedBaseUrl repoInfo causalHashJwt downloadedCallback
numDownloaded <- liftIO getNumDownloaded
pure (result, numDownloaded)
result & onLeft \err0 -> do
(Cli.returnEarly . Output.ShareError) case err0 of
Share.SyncError err -> Output.ShareErrorDownloadEntities err
Share.TransportError err -> Output.ShareErrorTransport err
Cli.respond (Output.DownloadedEntities numDownloaded)
loadShareLooseCodeIntoMemory :: ReadShareLooseCode -> Cli (Branch IO)
loadShareLooseCodeIntoMemory rrn@(ReadShareLooseCode {server, repo, path}) = do

View File

@ -178,8 +178,8 @@ data Input
CompileSchemeI String (HQ.HashQualified Name)
| -- generate scheme libraries
GenSchemeLibsI
| -- fetch scheme compiler from a given username
FetchSchemeCompilerI String
| -- fetch scheme compiler from a given username and branch
FetchSchemeCompilerI String String
| TestI TestInput
| -- metadata
-- `link metadata definitions` (adds metadata to all of `definitions`)
@ -226,7 +226,7 @@ data Input
| AuthLoginI
| VersionI
| DiffNamespaceToPatchI DiffNamespaceToPatchInput
| ProjectCreateI (Maybe ProjectName)
| ProjectCreateI Bool {- try downloading base? -} (Maybe ProjectName)
| ProjectRenameI ProjectName
| ProjectSwitchI ProjectAndBranchNames
| ProjectsI

View File

@ -131,6 +131,11 @@ data NumberedOutput
| ListBranches ProjectName [(ProjectBranchName, [(URI, ProjectName, ProjectBranchName)])]
| AmbiguousSwitch ProjectName (ProjectAndBranch ProjectName ProjectBranchName)
| AmbiguousReset AmbiguousReset'Argument (ProjectAndBranch Sqlite.Project Sqlite.ProjectBranch, Path.Path) (ProjectAndBranch ProjectName ProjectBranchName)
| -- | List all direct dependencies which don't have any names in the current branch
ListNamespaceDependencies
PPE.PrettyPrintEnv -- PPE containing names for everything from the root namespace.
Path.Absolute -- The namespace we're checking dependencies for.
(Map LabeledDependency (Set Name)) -- Mapping of external dependencies to their local dependents.
data AmbiguousReset'Argument
= AmbiguousReset'Hash
@ -296,11 +301,6 @@ data Output
| ListDependencies PPE.PrettyPrintEnv (Set LabeledDependency) [HQ.HashQualified Name] [HQ.HashQualified Name] -- types, terms
| -- | List dependents of a type or term.
ListDependents PPE.PrettyPrintEnv (Set LabeledDependency) [HQ.HashQualified Name] [HQ.HashQualified Name] -- types, terms
| -- | List all direct dependencies which don't have any names in the current branch
ListNamespaceDependencies
PPE.PrettyPrintEnv -- PPE containing names for everything from the root namespace.
Path.Absolute -- The namespace we're checking dependencies for.
(Map LabeledDependency (Set Name)) -- Mapping of external dependencies to their local dependents.
| DumpNumberedArgs NumberedArgs
| DumpBitBooster CausalHash (Map CausalHash [CausalHash])
| DumpUnisonFileHashes Int [(Name, Reference.Id)] [(Name, Reference.Id)] [(Name, Reference.Id)]
@ -373,6 +373,9 @@ data Output
| RenamedProject ProjectName ProjectName
| RenamedProjectBranch ProjectName ProjectBranchName ProjectBranchName
| CantRenameBranchTo ProjectBranchName
| FetchingLatestReleaseOfBase
| FailedToFetchLatestReleaseOfBase
| HappyCoding
-- | What did we create a project branch from?
--
@ -535,7 +538,6 @@ isFailure o = case o of
NoOp -> False
ListDependencies {} -> False
ListDependents {} -> False
ListNamespaceDependencies {} -> False
TermMissingType {} -> True
DumpUnisonFileHashes _ x y z -> x == mempty && y == mempty && z == mempty
NamespaceEmpty {} -> True
@ -588,6 +590,9 @@ isFailure o = case o of
RenamedProject {} -> False
RenamedProjectBranch {} -> False
CantRenameBranchTo {} -> True
FetchingLatestReleaseOfBase {} -> False
FailedToFetchLatestReleaseOfBase {} -> True
HappyCoding {} -> False
isNumberedFailure :: NumberedOutput -> Bool
isNumberedFailure = \case
@ -610,4 +615,5 @@ isNumberedFailure = \case
ShowDiffAfterPull {} -> False
ShowDiffAfterUndo {} -> False
ShowDiffNamespace {} -> False
ListNamespaceDependencies {} -> False
TodoOutput _ todo -> TO.todoScore todo > 0 || not (TO.noConflicts todo)

View File

@ -5,17 +5,16 @@ module Unison.Codebase.Editor.Propagate
)
where
import Control.Error.Util (hush)
import Control.Lens
import Control.Monad.Reader (ask)
import Data.Graph qualified as Graph
import Data.Map qualified as Map
import Data.Set qualified as Set
import U.Codebase.Sqlite.Queries qualified as Queries
import Unison.Builtin qualified as Builtin
import Unison.Cli.Monad (Cli)
import Unison.Cli.Monad qualified as Cli
import Unison.Cli.MonadUtils qualified as Cli
import Unison.Cli.TypeCheck qualified as Cli (typecheckFile')
import Unison.Codebase (Codebase)
import Unison.Codebase qualified as Codebase
import Unison.Codebase.Branch (Branch0 (..))
@ -33,7 +32,6 @@ import Unison.Codebase.TypeEdit qualified as TypeEdit
import Unison.ConstructorReference (GConstructorReference (..))
import Unison.DataDeclaration (Decl)
import Unison.DataDeclaration qualified as Decl
import Unison.FileParsers (synthesizeFile')
import Unison.Hash (Hash)
import Unison.Hashing.V2.Convert qualified as Hashing
import Unison.Name (Name)
@ -555,21 +553,12 @@ propagate patch b = case validatePatch patch of
)
)
mempty
typecheckResult <- typecheckFile codebase [] file
(runIdentity (Result.toMaybe typecheckResult) >>= hush)
typecheckResult <- Cli.typecheckFile' codebase [] file
runIdentity (Result.toMaybe typecheckResult)
& fmap UF.hashTerms
& (fmap . fmap) (\(_ann, ref, wk, tm, tp) -> (ref, wk, tm, tp))
& pure
typecheckFile ::
Codebase m Symbol Ann ->
[Type Symbol Ann] ->
UF.UnisonFile Symbol Ann ->
Sqlite.Transaction (Result.Result (Seq (Result.Note Symbol Ann)) (Either x (UF.TypecheckedUnisonFile Symbol Ann)))
typecheckFile codebase ambient file = do
typeLookup <- Codebase.typeLookupForDependencies codebase (UF.dependencies file)
pure . fmap Right $ synthesizeFile' ambient (typeLookup <> Builtin.typeLookup) file
-- TypecheckFile file ambient -> liftIO $ typecheck' ambient codebase file
unhashTypeComponent :: Reference -> Sqlite.Transaction (Map Symbol (Reference, Decl Symbol Ann))
unhashTypeComponent r = case Reference.toId r of

View File

@ -1,47 +0,0 @@
{-# LANGUAGE OverloadedStrings #-}
module Unison.Codebase.Editor.VersionParser where
import Data.Text (Text)
import Data.Text qualified as Text
import Data.Void (Void)
import Text.Megaparsec
import Text.Megaparsec.Char
import Unison.Codebase.Editor.RemoteRepo
import Unison.Codebase.Path qualified as Path
-- | Parse git version strings into valid unison namespaces.
--
-- >>> parseMaybe defaultBaseLib "release/M4"
-- >>> parseMaybe defaultBaseLib "release/M4b"
-- >>> parseMaybe defaultBaseLib "release/M4c.2"
-- Just (ReadShareLooseCode {server = DefaultCodeserver, repo = "unison", path = public.base.releases.M4})
-- Just (ReadShareLooseCode {server = DefaultCodeserver, repo = "unison", path = public.base.releases.M4b})
-- Just (ReadShareLooseCode {server = DefaultCodeserver, repo = "unison", path = public.base.releases.M4c_2})
-- >>> parseMaybe defaultBaseLib "dev/M4-1-g22ccb0b3b"
-- Just (ReadShareLooseCode {server = DefaultCodeserver, repo = "unison", path = public.base.main})
-- A version with the 'dirty' flag
-- >>> parseMaybe defaultBaseLib "dev/M3-409-gbcdf68db3'"
-- Just (ReadShareLooseCode {server = DefaultCodeserver, repo = "unison", path = public.base.main})
defaultBaseLib :: Parsec Void Text ReadShareLooseCode
defaultBaseLib = fmap makeNS $ release <|> unknown
where
unknown, release, milestoneVersion :: Parsec Void Text Text
unknown = pure "main" <* takeWhileP Nothing (const True) <* eof
release = fmap ("releases." <>) $ "release/" *> milestoneVersion <* eof
-- Parses the milestone of the current version; e.g. M4a -> M4
milestoneVersion = do
m <- char 'M'
milestoneVersion <- some digitChar
_minor <- many (alphaNumChar <|> ('_' <$ oneOf ['.', '_', '-']))
_dirty <- optional (char '\'')
pure . Text.pack $ m : milestoneVersion
makeNS :: Text -> ReadShareLooseCode
makeNS t =
ReadShareLooseCode
{ server = DefaultCodeserver,
repo = ShareUserHandle "unison",
path = "public" Path.:< "base" Path.:< Path.fromText t
}

View File

@ -8,17 +8,19 @@ import Control.Lens.Cons qualified as Cons
import Data.List (intercalate)
import Data.List.NonEmpty qualified as NE
import Data.Map qualified as Map
import Data.Maybe (fromJust)
import Data.Proxy (Proxy (..))
import Data.Set qualified as Set
import Data.Text qualified as Text
import Data.These (These (..))
import Network.URI qualified as URI
import System.Console.Haskeline.Completion (Completion (Completion))
import System.Console.Haskeline.Completion qualified as Haskeline
import Text.Megaparsec qualified as P
import U.Codebase.Sqlite.DbId (ProjectBranchId, ProjectId)
import U.Codebase.Sqlite.Project qualified as Sqlite
import U.Codebase.Sqlite.Queries qualified as Queries
import Unison.Cli.Pretty (prettyProjectNameSlash, prettySlashProjectBranchName)
import Unison.Cli.Pretty (prettyProjectNameSlash, prettySlashProjectBranchName, prettyURI)
import Unison.Cli.ProjectUtils qualified as ProjectUtils
import Unison.Codebase (Codebase)
import Unison.Codebase qualified as Codebase
@ -44,6 +46,7 @@ import Unison.CommandLine.Globbing qualified as Globbing
import Unison.CommandLine.InputPattern (ArgumentType (..), InputPattern (InputPattern), IsOptional (..))
import Unison.CommandLine.InputPattern qualified as I
import Unison.HashQualified qualified as HQ
import Unison.JitInfo qualified as JitInfo
import Unison.Name (Name)
import Unison.NameSegment qualified as NameSegment
import Unison.Prelude
@ -722,7 +725,7 @@ deleteProject =
InputPattern
{ patternName = "delete.project",
aliases = ["project.delete"],
visibility = I.Hidden,
visibility = I.Visible,
argTypes = [(Required, projectNameArg)],
help =
P.wrapColumn2
@ -740,7 +743,7 @@ deleteBranch =
InputPattern
{ patternName = "delete.branch",
aliases = ["branch.delete"],
visibility = I.Hidden,
visibility = I.Visible,
argTypes = [(Required, projectBranchNameWithOptionalProjectNameArg)],
help =
P.wrapColumn2
@ -1666,7 +1669,8 @@ helpTopicsMap =
("filestatus", fileStatusMsg),
("messages.disallowedAbsolute", disallowedAbsoluteMsg),
("remotes", remotesMsg),
("namespaces", pathnamesMsg)
("namespaces", pathnamesMsg),
("projects", projectsMsg)
]
where
blankline = ("", "")
@ -1790,6 +1794,30 @@ helpTopicsMap =
<> "If the project was created locally then the relationship will be established on"
<> "the first `push`."
]
projectsMsg =
P.lines $
[ P.wrap $
"A project is a versioned collection of code that can be edited, published, and depended on other projects."
<> "Unison projects are analogous to Git repositories.",
"",
P.column2
[ (patternName projectCreate, "create a new project"),
(patternName projectsInputPattern, "list all your projects"),
(patternName branchInputPattern, "create a new workstream"),
(patternName branchesInputPattern, "list all your branches"),
(patternName mergeLocal, "merge one branch into another"),
(patternName projectSwitch, "switch to a project or branch"),
(patternName push, "upload your changes to Unison Share"),
(patternName pull, "download code(/changes/updates) from Unison Share"),
(patternName clone, "download a Unison Share project or branch for contribution")
],
"",
tip ("Use" <> makeExample help [patternName projectCreate] <> "to learn more."),
"",
P.wrap $
"For full documentation, see"
<> prettyURI (fromJust (URI.parseURI "https://unison-lang.org/learn/projects"))
]
help :: InputPattern
help =
@ -1806,10 +1834,24 @@ help =
"\n\n"
showPatternHelp
visibleInputs
[isHelp -> Just msg] -> Left msg
[cmd] -> case Map.lookup cmd commandsByName of
Nothing -> Left . warn $ "I don't know of that command. Try `help`."
Just pat -> Left $ showPatternHelp pat
[cmd] ->
case (Map.lookup cmd commandsByName, isHelp cmd) of
(Nothing, Just msg) -> Left msg
(Nothing, Nothing) -> Left . warn $ "I don't know of that command. Try `help`."
(Just pat, Nothing) -> Left $ showPatternHelp pat
-- If we have a command and a help topic with the same name (like "projects"), then append a tip to the
-- command's help that suggests running `help-topic command`
(Just pat, Just _) ->
Left $
showPatternHelp pat
<> P.newline
<> P.newline
<> ( tip $
"To read more about"
<> P.group (P.string cmd <> ",")
<> "use"
<> makeExample helpTopics [P.string cmd]
)
_ -> Left $ warn "Use `help <cmd>` or `help`."
)
where
@ -2300,22 +2342,36 @@ fetchScheme =
[]
( P.wrapColumn2
[ ( makeExample fetchScheme [],
"Fetches the unison library for compiling to scheme.\n\n\
\This is done automatically when"
<> P.group (makeExample compileScheme [])
<> "is run\
\ if the library is not already in the standard location\
\ (unison.internal). However, this command will force\
\ a pull even if the library already exists. You can also specify\
\ a username to pull from (the default is `unison`) to use an alternate\
\ implementation of the scheme compiler. It will attempt to fetch\
\ [username].public.internal.trunk for use."
P.lines . fmap P.wrap $
[ "Fetches the unison library for compiling to scheme.",
"This is done automatically when"
<> P.group (makeExample compileScheme [])
<> "is run if the library is not already in the\
\ standard location (unison.internal). However,\
\ this command will force a pull even if the\
\ library already exists.",
"You can also specify a user and branch name to pull\
\ from in order to use an alternate version of the\
\ unison compiler (for development purposes, for\
\ example).",
"The default user is `unison`. The default branch\
\ for the `unison` user is a specified latest\
\ version of the compiler for stability. The\
\ default branch for other uses is `main`. The\
\ command fetches code from a project:",
P.indentN 2 ("@user/internal/branch")
]
)
]
)
( \case
[] -> pure (Input.FetchSchemeCompilerI "unison")
[name] -> pure (Input.FetchSchemeCompilerI name)
[] -> pure (Input.FetchSchemeCompilerI "unison" JitInfo.currentRelease)
[name] -> pure (Input.FetchSchemeCompilerI name branch)
where
branch
| name == "unison" = JitInfo.currentRelease
| otherwise = "main"
[name, branch] -> pure (Input.FetchSchemeCompilerI name branch)
_ -> Left $ showPatternHelp fetchScheme
)
@ -2430,7 +2486,7 @@ projectCreate =
InputPattern
{ patternName = "project.create",
aliases = ["create.project"],
visibility = I.Hidden,
visibility = I.Visible,
argTypes = [],
help =
P.wrapColumn2
@ -2441,8 +2497,28 @@ projectCreate =
[name] ->
case tryInto @ProjectName (Text.pack name) of
Left _ -> Left "Invalid project name."
Right name1 -> Right (Input.ProjectCreateI (Just name1))
_ -> Right (Input.ProjectCreateI Nothing)
Right name1 -> Right (Input.ProjectCreateI True (Just name1))
_ -> Right (Input.ProjectCreateI True Nothing)
}
projectCreateEmptyInputPattern :: InputPattern
projectCreateEmptyInputPattern =
InputPattern
{ patternName = "project.create-empty",
aliases = ["create.empty-project"],
visibility = I.Hidden,
argTypes = [],
help =
P.wrapColumn2
[ ("`project.create-empty`", "creates an empty project with a random name"),
("`project.create-empty foo`", "creates an empty project named `foo`")
],
parse = \case
[name] ->
case tryInto @ProjectName (Text.pack name) of
Left _ -> Left "Invalid project name."
Right name1 -> Right (Input.ProjectCreateI False (Just name1))
_ -> Right (Input.ProjectCreateI False Nothing)
}
projectRenameInputPattern :: InputPattern
@ -2450,7 +2526,7 @@ projectRenameInputPattern =
InputPattern
{ patternName = "project.rename",
aliases = ["rename.project"],
visibility = I.Hidden,
visibility = I.Visible,
argTypes = [],
help =
P.wrapColumn2
@ -2466,7 +2542,7 @@ projectSwitch =
InputPattern
{ patternName = "switch",
aliases = [],
visibility = I.Hidden,
visibility = I.Visible,
argTypes = [(Required, projectAndBranchNamesArg False)],
help =
P.wrapColumn2
@ -2487,7 +2563,7 @@ projectsInputPattern =
InputPattern
{ patternName = "projects",
aliases = ["list.project", "ls.project", "project.list"],
visibility = I.Hidden,
visibility = I.Visible,
argTypes = [],
help = P.wrap "List projects.",
parse = \_ -> Right Input.ProjectsI
@ -2498,7 +2574,7 @@ branchesInputPattern =
InputPattern
{ patternName = "branches",
aliases = ["list.branch", "ls.branch", "branch.list"],
visibility = I.Hidden,
visibility = I.Visible,
argTypes = [],
help =
P.wrapColumn2
@ -2516,7 +2592,7 @@ branchInputPattern =
InputPattern
{ patternName = "branch",
aliases = ["branch.create", "create.branch"],
visibility = I.Hidden,
visibility = I.Visible,
argTypes = [],
help =
P.wrapColumn2
@ -2547,7 +2623,7 @@ branchEmptyInputPattern =
InputPattern
{ patternName = "branch.empty",
aliases = ["branch.create-empty", "create.empty-branch"],
visibility = I.Hidden,
visibility = I.Visible,
argTypes = [],
help = P.wrap "Create a new empty branch.",
parse = \case
@ -2563,7 +2639,7 @@ branchRenameInputPattern =
InputPattern
{ patternName = "branch.rename",
aliases = ["rename.branch"],
visibility = I.Hidden,
visibility = I.Visible,
argTypes = [],
help =
P.wrapColumn2
@ -2579,7 +2655,7 @@ clone =
InputPattern
{ patternName = "clone",
aliases = [],
visibility = I.Hidden,
visibility = I.Visible,
argTypes = [],
help =
P.wrapColumn2
@ -2620,7 +2696,7 @@ releaseDraft =
InputPattern
{ patternName = "release.draft",
aliases = ["draft.release"],
visibility = I.Hidden,
visibility = I.Visible,
argTypes = [],
help = P.wrap "Draft a release.",
parse = \case
@ -2711,6 +2787,7 @@ validInputs =
previewUpdate,
printVersion,
projectCreate,
projectCreateEmptyInputPattern,
projectRenameInputPattern,
projectSwitch,
projectsInputPattern,

View File

@ -156,8 +156,7 @@ main dir welcome initialPath config initialInputs runtime sbRuntime codebase ser
loop currentRoot
loop Nothing
eventQueue <- Q.newIO
welcomeEvents <- Welcome.run codebase welcome
initialInputsRef <- newIORef $ welcomeEvents ++ initialInputs
initialInputsRef <- newIORef $ Welcome.run welcome ++ initialInputs
pageOutput <- newIORef True
cancelFileSystemWatch <- case shouldWatchFiles of
ShouldNotWatchFiles -> pure (pure ())

View File

@ -29,11 +29,7 @@ import Network.HTTP.Types qualified as Http
import Servant.Client qualified as Servant
import System.Console.ANSI qualified as ANSI
import System.Console.Haskeline.Completion qualified as Completion
import System.Directory
( canonicalizePath,
doesFileExist,
getHomeDirectory,
)
import System.Directory (canonicalizePath, doesFileExist, getHomeDirectory)
import U.Codebase.Branch (NamespaceStats (..))
import U.Codebase.Branch.Diff (NameChanges (..))
import U.Codebase.HashTags (CausalHash (..))
@ -43,6 +39,7 @@ import Unison.Auth.Types qualified as Auth
import Unison.Builtin.Decls qualified as DD
import Unison.Cli.Pretty
import Unison.Cli.ProjectUtils qualified as ProjectUtils
import Unison.Cli.ServantClientUtils qualified as ServantClientUtils
import Unison.Codebase.Editor.DisplayObject (DisplayObject (BuiltinObject, MissingObject, UserObject))
import Unison.Codebase.Editor.Input qualified as Input
import Unison.Codebase.Editor.Output
@ -490,6 +487,48 @@ notifyNumbered = \case
reset = IP.makeExample IP.reset
relPath0 = prettyPath' (Path.toPath' path)
absPath0 = review ProjectUtils.projectBranchPathPrism (ProjectAndBranch (pn0 ^. #projectId) (bn0 ^. #branchId), path)
ListNamespaceDependencies _ppe _path Empty -> ("This namespace has no external dependencies.", mempty)
ListNamespaceDependencies ppe path' externalDependencies ->
( P.column2Header (P.hiBlack "External dependency") ("Dependents in " <> prettyAbsolute path') $
List.intersperse spacer (externalDepsTable externalDependencies),
numberedArgs
)
where
spacer = ("", "")
(nameNumbers, numberedArgs) = numberedDependents externalDependencies
getNameNumber name = fromMaybe (error "ListNamespaceDependencies: name is missing number") (Map.lookup name nameNumbers)
numberedDependents :: Map LabeledDependency (Set Name) -> (Map Name Int, NumberedArgs)
numberedDependents deps =
deps
& Map.elems
& List.foldl'
( \(nextNum, (nameToNum, args)) names ->
let unnumberedNames = Set.toList $ Set.difference names (Map.keysSet nameToNum)
newNextNum = nextNum + length unnumberedNames
in ( newNextNum,
( nameToNum <> (Map.fromList (zip unnumberedNames [nextNum ..])),
args <> fmap Name.toString unnumberedNames
)
)
)
(1, (mempty, mempty))
& snd
externalDepsTable :: Map LabeledDependency (Set Name) -> [(P.Pretty P.ColorText, P.Pretty P.ColorText)]
externalDepsTable = ifoldMap $ \ld dependents ->
[(prettyLD ld, prettyDependents dependents)]
prettyLD :: LabeledDependency -> P.Pretty P.ColorText
prettyLD =
P.syntaxToColor
. prettyHashQualified
. LD.fold
(PPE.typeName ppe)
(PPE.termName ppe)
prettyDependents :: Set Name -> P.Pretty P.ColorText
prettyDependents refs =
refs
& Set.toList
& fmap (\name -> formatNum (getNameNumber name) <> prettyName name)
& P.lines
where
absPathToBranchId = Right
@ -1657,28 +1696,6 @@ notifyUser dir = \case
pure $ listDependentsOrDependencies ppe "Dependents" "dependents" lds types terms
ListDependencies ppe lds types terms ->
pure $ listDependentsOrDependencies ppe "Dependencies" "dependencies" lds types terms
ListNamespaceDependencies _ppe _path Empty -> pure $ "This namespace has no external dependencies."
ListNamespaceDependencies ppe path' externalDependencies -> do
let spacer = ("", "")
pure . P.column2Header (P.hiBlack "External dependency") ("Dependents in " <> prettyAbsolute path') $
List.intersperse spacer (externalDepsTable externalDependencies)
where
externalDepsTable :: Map LabeledDependency (Set Name) -> [(P.Pretty P.ColorText, P.Pretty P.ColorText)]
externalDepsTable = ifoldMap $ \ld dependents ->
[(prettyLD ld, prettyDependents dependents)]
prettyLD :: LabeledDependency -> P.Pretty P.ColorText
prettyLD =
P.syntaxToColor
. prettyHashQualified
. LD.fold
(PPE.typeName ppe)
(PPE.termName ppe)
prettyDependents :: Set Name -> P.Pretty P.ColorText
prettyDependents refs =
refs
& Set.toList
& fmap prettyName
& P.lines
DumpUnisonFileHashes hqLength datas effects terms ->
pure . P.syntaxToColor . P.lines $
( effects <&> \(n, r) ->
@ -1944,7 +1961,13 @@ notifyUser dir = \case
P.text ("Unauthorized: " <> message)
ServantClientError err ->
pure case err of
Servant.ConnectionError _exception -> P.wrap "Something went wrong with the connection. Try again?"
Servant.ConnectionError exception ->
P.wrap $
fromMaybe "Something went wrong with the connection. Try again?" do
case ServantClientUtils.classifyConnectionError exception of
ServantClientUtils.ConnectionError'Offline -> Just "You appear to be offline."
ServantClientUtils.ConnectionError'SomethingElse _ -> Nothing
ServantClientUtils.ConnectionError'SomethingEntirelyUnexpected _ -> Nothing
Servant.DecodeFailure message response ->
P.wrap "Huh, I failed to decode a response from the server."
<> P.newline
@ -2092,6 +2115,34 @@ notifyUser dir = \case
CantRenameBranchTo branch ->
pure . P.wrap $
"You can't rename a branch to" <> P.group (prettyProjectBranchName branch <> ".")
FetchingLatestReleaseOfBase ->
pure . P.wrap $
"I'll now fetch the latest version of the base Unison library..."
FailedToFetchLatestReleaseOfBase ->
pure . P.wrap $ "Sorry something went wrong while fetching the library."
HappyCoding -> do
pure $
P.wrap "🎨 Type `ui` to explore this project's code in your browser."
<> P.newline
<> P.wrap ("🔭 Discover libraries at https://share.unison-lang.org")
<> P.newline
<> P.wrap "📖 Use `help-topic projects` to learn more about projects."
<> P.newline
<> P.newline
<> P.wrap "Write your first Unison code with UCM:"
<> P.newline
<> P.newline
<> P.indentN
2
( P.wrap "1. Open scratch.u."
<> P.newline
<> P.wrap "2. Write some Unison code and save the file."
<> P.newline
<> P.wrap "3. In UCM, type `add` to save it to your new project."
)
<> P.newline
<> P.newline
<> P.wrap "🎉 🥳 Happy coding!"
where
_nameChange _cmd _pastTenseCmd _oldName _newName _r = error "todo"

View File

@ -1,35 +1,22 @@
module Unison.CommandLine.Welcome where
module Unison.CommandLine.Welcome
( CodebaseInitStatus (..),
Welcome (..),
asciiartUnison,
run,
welcome,
)
where
import Data.Sequence (singleton)
import Data.These (These (..))
import System.Random (randomRIO)
import Unison.Codebase (Codebase)
import Unison.Codebase qualified as Codebase
import Unison.Codebase.Editor.Input
import Unison.Codebase.Editor.RemoteRepo (ReadRemoteNamespace (..), ReadShareLooseCode (..))
import Unison.Codebase.Path (Path)
import Unison.Codebase.Path qualified as Path
import Unison.Codebase.SyncMode qualified as SyncMode
import Unison.Codebase.Verbosity qualified as Verbosity
import Unison.CommandLine.Types (ShouldWatchFiles (..))
import Unison.NameSegment (NameSegment (NameSegment))
import Unison.Prelude
import Unison.Util.Pretty qualified as P
import Prelude hiding (readFile, writeFile)
data Welcome = Welcome
{ onboarding :: Onboarding, -- Onboarding States
downloadBase :: DownloadBase,
watchDir :: FilePath,
unisonVersion :: Text,
shouldWatchFiles :: ShouldWatchFiles
unisonVersion :: Text
}
data DownloadBase
= DownloadBase ReadShareLooseCode
| DontDownloadBase
deriving (Show, Eq)
-- Previously Created is different from Previously Onboarded because a user can
-- 1.) create a new codebase
-- 2.) decide not to go through the onboarding flow until later and exit
@ -40,80 +27,44 @@ data CodebaseInitStatus
deriving (Show, Eq)
data Onboarding
= Init CodebaseInitStatus -- Can transition to [DownloadingBase, Author, Finished, PreviouslyOnboarded]
| DownloadingBase ReadShareLooseCode -- Can transition to [Author, Finished]
= Init CodebaseInitStatus -- Can transition to [Author, Finished, PreviouslyOnboarded]
| Author -- Can transition to [Finished]
-- End States
| Finished
| PreviouslyOnboarded
deriving (Show, Eq)
welcome :: CodebaseInitStatus -> DownloadBase -> FilePath -> Text -> ShouldWatchFiles -> Welcome
welcome initStatus downloadBase filePath unisonVersion shouldWatchFiles =
Welcome (Init initStatus) downloadBase filePath unisonVersion shouldWatchFiles
welcome :: CodebaseInitStatus -> Text -> Welcome
welcome initStatus unisonVersion =
Welcome (Init initStatus) unisonVersion
pullBase :: ReadShareLooseCode -> Either Event Input
pullBase ns =
let seg = NameSegment "base"
rootPath = Path.Path {Path.toSeq = singleton seg}
abs = Path.Absolute {Path.unabsolute = rootPath}
pullRemote =
PullRemoteBranchI
( PullSourceTarget2
(ReadShare'LooseCode ns)
(This (Path.Path' {Path.unPath' = Left abs}))
)
SyncMode.Complete
PullWithHistory
Verbosity.Silent
in Right pullRemote
run :: Codebase IO v a -> Welcome -> IO [Either Event Input]
run codebase Welcome {onboarding = onboarding, downloadBase = downloadBase, watchDir = dir, unisonVersion = version, shouldWatchFiles} = do
run :: Welcome -> [Either Event Input]
run Welcome {onboarding = onboarding, unisonVersion = version} = do
go onboarding []
where
go :: Onboarding -> [Either Event Input] -> IO [Either Event Input]
go :: Onboarding -> [Either Event Input] -> [Either Event Input]
go onboarding acc =
case onboarding of
Init NewlyCreatedCodebase -> do
determineFirstStep downloadBase codebase >>= \step -> go step (headerMsg : acc)
go PreviouslyOnboarded (headerMsg : acc)
where
headerMsg = toInput (header version)
Init PreviouslyCreatedCodebase -> do
go PreviouslyOnboarded (headerMsg : acc)
where
headerMsg = toInput (header version)
DownloadingBase ns@(ReadShareLooseCode {path}) ->
go Author ([pullBaseInput, downloadMsg] ++ acc)
where
downloadMsg = Right $ CreateMessage (downloading path)
pullBaseInput = pullBase ns
Author ->
go Finished (authorMsg : acc)
where
authorMsg = toInput authorSuggestion
-- These are our two terminal Welcome conditions, at the end we reverse the order of the desired input commands otherwise they come out backwards
Finished -> do
startMsg <- getStarted shouldWatchFiles dir
pure $ reverse (toInput startMsg : acc)
PreviouslyOnboarded -> do
startMsg <- getStarted shouldWatchFiles dir
pure $ reverse (toInput startMsg : acc)
Finished -> reverse (toInput getStarted : acc)
PreviouslyOnboarded -> reverse (toInput getStarted : acc)
toInput :: P.Pretty P.ColorText -> Either Event Input
toInput pretty =
Right $ CreateMessage pretty
determineFirstStep :: DownloadBase -> Codebase IO v a -> IO Onboarding
determineFirstStep downloadBase codebase = do
isEmptyCodebase <- Codebase.runTransaction codebase Codebase.getRootBranchExists
case downloadBase of
DownloadBase ns
| isEmptyCodebase ->
pure $ DownloadingBase ns
_ ->
pure PreviouslyOnboarded
asciiartUnison :: P.Pretty P.ColorText
asciiartUnison =
P.red " _____"
@ -139,20 +90,6 @@ asciiartUnison =
<> P.cyan "|___|"
<> P.purple "_|_|"
downloading :: Path -> P.Pretty P.ColorText
downloading path =
P.lines
[ P.group (P.wrap "🐣 Since this is a fresh codebase, let me download the base library for you." <> P.newline),
P.wrap
( "🕐 Downloading"
<> P.blue (P.string (show path))
<> "of the"
<> P.bold "base library"
<> "into"
<> P.group (P.blue ".base" <> ", this may take a minute...")
)
]
header :: Text -> P.Pretty P.ColorText
header version =
asciiartUnison
@ -173,22 +110,9 @@ authorSuggestion =
P.wrap $ P.blue "https://www.unison-lang.org/learn/tooling/configuration/"
]
getStarted :: ShouldWatchFiles -> FilePath -> IO (P.Pretty P.ColorText)
getStarted shouldWatchFiles dir = do
earth <- (["🌎", "🌍", "🌏"] !!) <$> randomRIO (0, 2)
pure $
P.linesSpaced
[ P.wrap "Get started:",
P.indentN 2 $
P.column2
( [ ("📖", "Type " <> P.hiBlue "help" <> " to list all commands, or " <> P.hiBlue "help <cmd>" <> " to view help for one command"),
("🎨", "Type " <> P.hiBlue "ui" <> " to open the Codebase UI in your default browser"),
("📚", "Read the official docs at " <> P.blue "https://www.unison-lang.org/learn/"),
(earth, "Visit Unison Share at " <> P.blue "https://share.unison-lang.org" <> " to discover libraries")
]
<> case shouldWatchFiles of
ShouldWatchFiles -> [("👀", "I'm watching for changes to " <> P.bold ".u" <> " files under " <> (P.group . P.blue $ P.string dir))]
ShouldNotWatchFiles -> [("📝", "File watching is disabled, use the 'load' command to parse and typecheck unison files.")]
)
]
getStarted :: P.Pretty P.ColorText
getStarted =
P.wrap "📚 Read the official docs at https://www.unison-lang.org/learn/"
<> P.newline
<> P.newline
<> P.wrap "Type 'project.create' to get started."

View File

@ -0,0 +1,4 @@
module Unison.JitInfo (currentRelease) where
currentRelease :: String
currentRelease = "releases/0.0.1"

View File

@ -9,7 +9,6 @@ import Unison.Test.Cli.Monad qualified as Cli.Monad
import Unison.Test.GitSync qualified as GitSync
import Unison.Test.LSP qualified as LSP
import Unison.Test.UriParser qualified as UriParser
import Unison.Test.VersionParser qualified as VersionParser
test :: Test ()
test =
@ -18,8 +17,7 @@ test =
ClearCache.test,
Cli.Monad.test,
GitSync.test,
UriParser.test,
VersionParser.test
UriParser.test
]
main :: IO ()

View File

@ -1,37 +0,0 @@
{-# LANGUAGE OverloadedStrings #-}
module Unison.Test.VersionParser where
import Control.Error.Safe (rightMay)
import Data.Text
import EasyTest
import Text.Megaparsec
import Unison.Codebase.Editor.RemoteRepo
import Unison.Codebase.Editor.VersionParser
import Unison.Codebase.Path qualified as Path
test :: Test ()
test =
scope "versionparser" . tests . fmap makeTest $
[ ("latest-abc", "main"),
("dev/M4", "main"), -- or should this be "releases.M4"?
("dev/M4-1-g22ccb0b3b", "main"), -- and should this also be "releases.m4"?
-- All non-dev releases should pull from the most recent major milestone
("release/M4", "releases.M4"),
("release/M2i_3", "releases.M2"),
("release/M2i-HOTFIX", "releases.M2")
]
makeTest :: (Text, Text) -> Test ()
makeTest (version, path) =
scope (unpack version) $
expectEqual
(rightMay $ runParser defaultBaseLib "versionparser" version)
( Just
( ReadShareLooseCode
{ server = DefaultCodeserver,
repo = ShareUserHandle "unison",
path = Path.fromList ["public", "base"] <> Path.fromText path
}
)
)

View File

@ -37,6 +37,7 @@ library
Unison.Cli.Pretty
Unison.Cli.PrettyPrintUtils
Unison.Cli.ProjectUtils
Unison.Cli.ServantClientUtils
Unison.Cli.Share.Projects
Unison.Cli.Share.Projects.Types
Unison.Cli.TypeCheck
@ -75,7 +76,6 @@ library
Unison.Codebase.Editor.TodoOutput
Unison.Codebase.Editor.UCMVersion
Unison.Codebase.Editor.UriParser
Unison.Codebase.Editor.VersionParser
Unison.Codebase.TranscriptParser
Unison.CommandLine
Unison.CommandLine.Completion
@ -88,6 +88,7 @@ library
Unison.CommandLine.OutputMessages
Unison.CommandLine.Types
Unison.CommandLine.Welcome
Unison.JitInfo
Unison.LSP
Unison.LSP.CancelRequest
Unison.LSP.CodeAction
@ -132,6 +133,7 @@ library
InstanceSigs
LambdaCase
MultiParamTypeClasses
MultiWayIf
NamedFieldPuns
NumericUnderscores
OverloadedLabels
@ -261,6 +263,7 @@ executable cli-integration-tests
InstanceSigs
LambdaCase
MultiParamTypeClasses
MultiWayIf
NamedFieldPuns
NumericUnderscores
OverloadedLabels
@ -390,6 +393,7 @@ executable transcripts
InstanceSigs
LambdaCase
MultiParamTypeClasses
MultiWayIf
NamedFieldPuns
NumericUnderscores
OverloadedLabels
@ -523,6 +527,7 @@ executable unison
InstanceSigs
LambdaCase
MultiParamTypeClasses
MultiWayIf
NamedFieldPuns
NumericUnderscores
OverloadedLabels
@ -638,7 +643,6 @@ test-suite cli-tests
Unison.Test.LSP
Unison.Test.Ucm
Unison.Test.UriParser
Unison.Test.VersionParser
hs-source-dirs:
tests
default-extensions:
@ -662,6 +666,7 @@ test-suite cli-tests
InstanceSigs
LambdaCase
MultiParamTypeClasses
MultiWayIf
NamedFieldPuns
NumericUnderscores
OverloadedLabels

View File

@ -80,11 +80,6 @@ data ShouldForkCodebase
| DontFork
deriving (Show, Eq)
data ShouldDownloadBase
= ShouldDownloadBase
| ShouldNotDownloadBase
deriving (Show, Eq)
data ShouldSaveCodebase
= SaveCodebase (Maybe FilePath)
| DontSaveCodebase
@ -109,7 +104,6 @@ data Command
= Launch
IsHeadless
CodebaseServerOpts
ShouldDownloadBase
-- Starting path
(Maybe Path.Absolute)
ShouldWatchFiles
@ -359,10 +353,9 @@ launchParser :: CodebaseServerOpts -> IsHeadless -> Parser Command
launchParser envOpts isHeadless = do
-- ApplicativeDo
codebaseServerOpts <- codebaseServerOptsParser envOpts
downloadBase <- downloadBaseFlag
startingPath <- startingPathOption
shouldWatchFiles <- noFileWatchFlag
pure (Launch isHeadless codebaseServerOpts downloadBase startingPath shouldWatchFiles)
pure (Launch isHeadless codebaseServerOpts startingPath shouldWatchFiles)
initParser :: Parser Command
initParser = pure Init
@ -421,18 +414,6 @@ saveCodebaseToFlag = do
_ -> DontSaveCodebase
)
downloadBaseFlag :: Parser ShouldDownloadBase
downloadBaseFlag =
flag
ShouldDownloadBase
ShouldNotDownloadBase
( long "no-base"
<> help downloadBaseHelp
<> noGlobal
)
where
downloadBaseHelp = "if set, a new codebase will be created without downloading the base library, otherwise the new codebase will download base"
startingPathOption :: Parser (Maybe Path.Absolute)
startingPathOption =
let meta =

View File

@ -7,7 +7,10 @@
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-}
module Main where
module Main
( main,
)
where
import ArgParse
( CodebasePathOption (..),
@ -15,7 +18,6 @@ import ArgParse
GlobalOptions (GlobalOptions, codebasePathOption, exitOption),
IsHeadless (Headless, WithCLI),
RunSource (..),
ShouldDownloadBase (..),
ShouldExit (DoNotExit, Exit),
ShouldForkCodebase (..),
ShouldSaveCodebase (..),
@ -25,7 +27,6 @@ import ArgParse
import Compat (defaultInterruptHandler, withInterruptHandler)
import Control.Concurrent (newEmptyMVar, runInUnboundThread, takeMVar)
import Control.Concurrent.STM
import Control.Error.Safe (rightMay)
import Control.Exception (evaluate)
import Data.ByteString.Lazy qualified as BL
import Data.Configurator.Types (Config)
@ -37,13 +38,11 @@ import Data.Text.IO qualified as Text
import GHC.Conc (setUncaughtExceptionHandler)
import GHC.Conc qualified
import Ki qualified
import Language.Haskell.TH qualified as TH
import Language.Haskell.TH.Syntax qualified as TH
import Network.HTTP.Client qualified as HTTP
import Network.HTTP.Client.TLS qualified as HTTP
import Stats (recordRtsStats)
import System.Directory (canonicalizePath, getCurrentDirectory, removeDirectoryRecursive)
import System.Environment (getProgName, lookupEnv, withArgs)
import System.Environment (getProgName, withArgs)
import System.Exit qualified as Exit
import System.FilePath qualified as FP
import System.IO (stderr)
@ -51,16 +50,12 @@ import System.IO.CodePage (withCP65001)
import System.IO.Error (catchIOError)
import System.IO.Temp qualified as Temp
import System.Path qualified as Path
import Text.Megaparsec (runParser)
import Text.Pretty.Simple (pHPrint)
import U.Codebase.Sqlite.Queries qualified as Queries
import Unison.Codebase (Codebase, CodebasePath)
import Unison.Codebase qualified as Codebase
import Unison.Codebase.Branch (Branch)
import Unison.Codebase.Editor.Input qualified as Input
import Unison.Codebase.Editor.RemoteRepo (ReadShareLooseCode)
import Unison.Codebase.Editor.UriParser (parseReadShareLooseCode)
import Unison.Codebase.Editor.VersionParser qualified as VP
import Unison.Codebase.Execute (execute)
import Unison.Codebase.Init (CodebaseInitOptions (..), InitError (..), InitResult (..), SpecifiedCodebase (..))
import Unison.Codebase.Init qualified as CodebaseInit
@ -147,7 +142,6 @@ main = withCP65001 . runInUnboundThread . Ki.scoped $ \scope -> do
[Left fileEvent, Right $ Input.ExecuteI mainName args, Right Input.QuitI]
serverUrl
startPath
ShouldNotDownloadBase
initRes
noOpRootNotifier
noOpPathNotifier
@ -173,7 +167,6 @@ main = withCP65001 . runInUnboundThread . Ki.scoped $ \scope -> do
[Left fileEvent, Right $ Input.ExecuteI mainName args, Right Input.QuitI]
serverUrl
startPath
ShouldNotDownloadBase
initRes
noOpRootNotifier
noOpPathNotifier
@ -247,7 +240,7 @@ main = withCP65001 . runInUnboundThread . Ki.scoped $ \scope -> do
case mrtsStatsFp of
Nothing -> action
Just fp -> recordRtsStats fp action
Launch isHeadless codebaseServerOpts downloadBase mayStartingPath shouldWatchFiles -> do
Launch isHeadless codebaseServerOpts mayStartingPath shouldWatchFiles -> do
getCodebaseOrExit mCodePathOption (SC.MigrateAfterPrompt SC.Backup SC.Vacuum) \(initRes, _, theCodebase) -> do
withRuntimes RTI.Persistent \(runtime, sbRuntime) -> do
startingPath <- case isHeadless of
@ -308,7 +301,6 @@ main = withCP65001 . runInUnboundThread . Ki.scoped $ \scope -> do
[]
(Just baseUrl)
(Just startingPath)
downloadBase
initRes
notifyOnRootChanges
notifyOnPathChanges
@ -479,22 +471,18 @@ launch ::
[Either Input.Event Input.Input] ->
Maybe Server.BaseUrl ->
Maybe Path.Absolute ->
ShouldDownloadBase ->
InitResult ->
(Branch IO -> STM ()) ->
(Path.Absolute -> STM ()) ->
CommandLine.ShouldWatchFiles ->
IO ()
launch dir config runtime sbRuntime codebase inputs serverBaseUrl mayStartingPath shouldDownloadBase initResult notifyRootChange notifyPathChange shouldWatchFiles =
let downloadBase = case defaultBaseLib of
Just remoteNS | shouldDownloadBase == ShouldDownloadBase -> Welcome.DownloadBase remoteNS
_ -> Welcome.DontDownloadBase
isNewCodebase = case initResult of
CreatedCodebase {} -> NewlyCreatedCodebase
_ -> PreviouslyCreatedCodebase
launch dir config runtime sbRuntime codebase inputs serverBaseUrl mayStartingPath initResult notifyRootChange notifyPathChange shouldWatchFiles =
let isNewCodebase = case initResult of
CreatedCodebase -> NewlyCreatedCodebase
OpenedCodebase -> PreviouslyCreatedCodebase
(ucmVersion, _date) = Version.gitDescribe
welcome = Welcome.welcome isNewCodebase downloadBase dir ucmVersion shouldWatchFiles
welcome = Welcome.welcome isNewCodebase ucmVersion
in CommandLine.main
dir
welcome
@ -521,27 +509,9 @@ markdownFile md = case FP.takeExtension md of
isDotU :: String -> Bool
isDotU file = FP.takeExtension file == ".u"
-- so we can do `ucm --help`, `ucm -help` or `ucm help` (I hate
-- having to remember which one is supported)
isFlag :: String -> String -> Bool
isFlag f arg = arg == f || arg == "-" ++ f || arg == "--" ++ f
getConfigFilePath :: Maybe FilePath -> IO FilePath
getConfigFilePath mcodepath = (FP.</> ".unisonConfig") <$> Codebase.getCodebaseDir mcodepath
defaultBaseLib :: Maybe ReadShareLooseCode
defaultBaseLib =
let mayBaseSharePath =
$( do
mayPath <- TH.runIO (lookupEnv "UNISON_BASE_PATH")
TH.lift mayPath
)
in mayBaseSharePath & \case
Just s -> eitherToMaybe $ parseReadShareLooseCode "UNISON_BASE_PATH" s
Nothing -> rightMay $ runParser VP.defaultBaseLib "version" gitRef
where
(gitRef, _date) = Version.gitDescribe
getCodebaseOrExit :: Maybe CodebasePathOption -> SC.MigrationStrategy -> ((InitResult, CodebasePath, Codebase IO Symbol Ann) -> IO r) -> IO r
getCodebaseOrExit codebasePathOption migrationStrategy action = do
initOptions <- argsToCodebaseInitOptions codebasePathOption

View File

@ -96,3 +96,4 @@ default-extensions:
- TypeApplications
- TypeOperators
- ViewPatterns
- ImportQualifiedPost

View File

@ -31,7 +31,7 @@ module Unison.Server.Backend
displayType,
docsInBranchToHtmlFiles,
expandShortCausalHash,
findDocInBranchAndRender,
findDocInBranch,
formatSuffixedType,
getCurrentParseNames,
getCurrentPrettyNames,
@ -454,25 +454,16 @@ lsAtPath codebase mayRootBranch absPath = do
b <- Codebase.runTransaction codebase (Codebase.getShallowBranchAtPath (Path.unabsolute absPath) mayRootBranch)
lsBranch codebase b
findDocInBranchAndRender ::
findDocInBranch ::
Set NameSegment ->
Width ->
Rt.Runtime Symbol ->
Codebase IO Symbol Ann ->
PPED.PrettyPrintEnvDecl ->
V2Branch.Branch m ->
Backend IO (Maybe Doc.Doc)
findDocInBranchAndRender names _width runtime codebase ppe namespaceBranch =
let renderReadme :: PPED.PrettyPrintEnvDecl -> Reference -> IO Doc.Doc
renderReadme ppe docReference = do
doc <- evalDocRef runtime codebase docReference <&> Doc.renderDoc ppe
pure doc
-- choose the first term (among conflicted terms) matching any of these names, in this order.
Maybe TermReference
findDocInBranch names namespaceBranch =
let -- choose the first term (among conflicted terms) matching any of these names, in this order.
-- we might later want to return all of them to let the front end decide
toCheck = Set.toList names
readme :: Maybe Reference
readme = listToMaybe $ do
readmeRef :: Maybe Reference
readmeRef = listToMaybe $ do
name <- toCheck
term <- toList $ Map.lookup name termsMap
k <- Map.keys term
@ -482,8 +473,7 @@ findDocInBranchAndRender names _width runtime codebase ppe namespaceBranch =
V2Referent.Ref ref -> pure $ Cv.reference2to1 ref
where
termsMap = V2Branch.terms namespaceBranch
in liftIO $ do
traverse (renderReadme ppe) readme
in readmeRef
isDoc :: Codebase m Symbol Ann -> Referent.Referent -> Sqlite.Transaction Bool
isDoc codebase ref = do

View File

@ -11,11 +11,9 @@
module Unison.Server.Local.Endpoints.NamespaceDetails where
import Control.Monad.Except
import Data.Aeson
import Data.OpenApi (ToSchema)
import Data.Set qualified as Set
import Servant (Capture, QueryParam, (:>))
import Servant.Docs (DocCapture (..), ToCapture (..), ToSample (..))
import Servant.Docs (DocCapture (..), ToCapture (..))
import Servant.OpenApi ()
import U.Codebase.Causal qualified as V2Causal
import U.Codebase.HashTags (CausalHash)
@ -28,11 +26,10 @@ import Unison.Parser.Ann (Ann)
import Unison.Prelude
import Unison.Server.Backend
import Unison.Server.Backend qualified as Backend
import Unison.Server.Doc (Doc)
import Unison.Server.Doc qualified as Doc
import Unison.Server.Types
( APIGet,
UnisonHash,
mayDefaultWidth,
NamespaceDetails (..),
v2CausalBranchToUnisonHash,
)
import Unison.Symbol (Symbol)
@ -51,34 +48,6 @@ instance ToCapture (Capture "namespace" Text) where
"namespace"
"The fully qualified name of a namespace. The leading `.` is optional."
instance ToSample NamespaceDetails where
toSamples _ =
[ ( "When no value is provided for `namespace`, the root namespace `.` is "
<> "listed by default",
NamespaceDetails
Path.empty
"#gjlk0dna8dongct6lsd19d1o9hi5n642t8jttga5e81e91fviqjdffem0tlddj7ahodjo5"
Nothing
)
]
data NamespaceDetails = NamespaceDetails
{ fqn :: Path.Path,
hash :: UnisonHash,
readme :: Maybe Doc
}
deriving (Generic, Show)
instance ToJSON NamespaceDetails where
toJSON NamespaceDetails {..} =
object
[ "fqn" .= fqn,
"hash" .= hash,
"readme" .= readme
]
deriving instance ToSchema NamespaceDetails
namespaceDetails ::
Rt.Runtime Symbol ->
Codebase IO Symbol Ann ->
@ -86,33 +55,26 @@ namespaceDetails ::
Maybe (Either ShortCausalHash CausalHash) ->
Maybe Width ->
Backend IO NamespaceDetails
namespaceDetails runtime codebase namespacePath mayRoot mayWidth =
let width = mayDefaultWidth mayWidth
in do
(rootCausal, namespaceCausal, shallowBranch) <-
Backend.hoistBackend (Codebase.runTransaction codebase) do
rootCausalHash <-
case mayRoot of
Nothing -> Backend.resolveRootBranchHashV2 Nothing
Just (Left sch) -> Backend.resolveRootBranchHashV2 (Just sch)
Just (Right ch) -> lift $ Backend.resolveCausalHashV2 (Just ch)
-- lift (Backend.resolveCausalHashV2 rootCausalHash)
namespaceCausal <- lift $ Codebase.getShallowCausalAtPath namespacePath (Just rootCausalHash)
shallowBranch <- lift $ V2Causal.value namespaceCausal
pure (rootCausalHash, namespaceCausal, shallowBranch)
namespaceDetails <- do
(_localNamesOnly, ppe) <- Backend.scopedNamesForBranchHash codebase (Just rootCausal) namespacePath
readme <-
Backend.findDocInBranchAndRender
readmeNames
width
runtime
codebase
ppe
shallowBranch
let causalHash = v2CausalBranchToUnisonHash namespaceCausal
pure $ NamespaceDetails namespacePath causalHash readme
pure $ namespaceDetails
namespaceDetails runtime codebase namespacePath mayRoot _mayWidth = do
(rootCausal, namespaceCausal, shallowBranch) <-
Backend.hoistBackend (Codebase.runTransaction codebase) do
rootCausalHash <-
case mayRoot of
Nothing -> Backend.resolveRootBranchHashV2 Nothing
Just (Left sch) -> Backend.resolveRootBranchHashV2 (Just sch)
Just (Right ch) -> lift $ Backend.resolveCausalHashV2 (Just ch)
-- lift (Backend.resolveCausalHashV2 rootCausalHash)
namespaceCausal <- lift $ Codebase.getShallowCausalAtPath namespacePath (Just rootCausalHash)
shallowBranch <- lift $ V2Causal.value namespaceCausal
pure (rootCausalHash, namespaceCausal, shallowBranch)
namespaceDetails <- do
(_localNamesOnly, ppe) <- Backend.scopedNamesForBranchHash codebase (Just rootCausal) namespacePath
let mayReadmeRef = Backend.findDocInBranch readmeNames shallowBranch
renderedReadme <- for mayReadmeRef \readmeRef -> do
eDoc <- liftIO $ evalDocRef runtime codebase readmeRef
pure $ Doc.renderDoc ppe eDoc
let causalHash = v2CausalBranchToUnisonHash namespaceCausal
pure $ NamespaceDetails namespacePath causalHash renderedReadme
pure $ namespaceDetails
where
readmeNames = Set.fromList ["README", "Readme", "ReadMe", "readme"]

View File

@ -0,0 +1,38 @@
module Unison.Server.Share.NamespaceDetails (namespaceDetails) where
import Control.Monad.Except
import Data.Set qualified as Set
import Servant.OpenApi ()
import U.Codebase.HashTags (CausalHash)
import Unison.Codebase (Codebase)
import Unison.Codebase qualified as Codebase
import Unison.Codebase.Path qualified as Path
import Unison.Codebase.Runtime qualified as Rt
import Unison.Parser.Ann (Ann)
import Unison.Server.Backend
import Unison.Server.Backend qualified as Backend
import Unison.Server.Share.RenderDoc qualified as RenderDoc
import Unison.Server.Types
( NamespaceDetails (..),
v2CausalBranchToUnisonHash,
)
import Unison.Symbol (Symbol)
import Unison.Util.Pretty (Width)
namespaceDetails ::
Rt.Runtime Symbol ->
Codebase IO Symbol Ann ->
Path.Path ->
CausalHash ->
Maybe Width ->
Backend IO NamespaceDetails
namespaceDetails runtime codebase namespacePath rootCausalHash mayWidth = do
causalHashAtPath <- liftIO $ Codebase.runTransaction codebase do
causalBranch <- Backend.resolveCausalHashV2 (Just rootCausalHash)
namespaceCausal <- Codebase.getShallowCausalAtPath namespacePath (Just causalBranch)
let causalHashAtPath = v2CausalBranchToUnisonHash namespaceCausal
pure causalHashAtPath
mayReadme <- RenderDoc.findAndRenderDoc readmeNames runtime codebase namespacePath rootCausalHash mayWidth
pure $ NamespaceDetails namespacePath causalHashAtPath mayReadme
where
readmeNames = Set.fromList ["README", "Readme", "ReadMe", "readme"]

View File

@ -11,55 +11,52 @@
module Unison.Server.Share.RenderDoc where
import Control.Monad.Except
import Data.Set qualified as Set
import Servant.OpenApi ()
import U.Codebase.Causal qualified as V2Causal
import U.Codebase.HashTags (CausalHash)
import U.Codebase.Sqlite.NameLookups (PathSegments (..))
import U.Codebase.Sqlite.Operations qualified as Ops
import Unison.Codebase (Codebase)
import Unison.Codebase qualified as Codebase
import Unison.Codebase.Path qualified as Path
import Unison.Codebase.Runtime qualified as Rt
import Unison.Codebase.ShortCausalHash (ShortCausalHash)
import Unison.NameSegment (NameSegment)
import Unison.LabeledDependency qualified as LD
import Unison.NameSegment (NameSegment (..))
import Unison.Parser.Ann (Ann)
import Unison.Prelude
import Unison.PrettyPrintEnvDecl.Sqlite qualified as PPESqlite
import Unison.Server.Backend
import Unison.Server.Backend qualified as Backend
import Unison.Server.Doc (Doc)
import Unison.Server.Types
( mayDefaultWidth,
)
import Unison.Server.Doc qualified as Doc
import Unison.Symbol (Symbol)
import Unison.Util.Pretty (Width)
renderDoc ::
-- | Find, eval, and render the first doc we find with any of the provided names within the given namespace
-- If no doc is found, return Nothing
--
-- Requires Name Lookups, currently only usable on Share.
findAndRenderDoc ::
Set NameSegment ->
Rt.Runtime Symbol ->
Codebase IO Symbol Ann ->
Path.Path ->
Maybe (Either ShortCausalHash CausalHash) ->
CausalHash ->
Maybe Width ->
Backend IO (Maybe Doc)
renderDoc docNames runtime codebase namespacePath mayRoot mayWidth =
let width = mayDefaultWidth mayWidth
in do
(rootCausal, shallowBranch) <-
Backend.hoistBackend (Codebase.runTransaction codebase) do
rootCausalHash <-
case mayRoot of
Nothing -> Backend.resolveRootBranchHashV2 Nothing
Just (Left sch) -> Backend.resolveRootBranchHashV2 (Just sch)
Just (Right ch) -> lift $ Backend.resolveCausalHashV2 (Just ch)
-- lift (Backend.resolveCausalHashV2 rootCausalHash)
namespaceCausal <- lift $ Codebase.getShallowCausalAtPath namespacePath (Just rootCausalHash)
shallowBranch <- lift $ V2Causal.value namespaceCausal
pure (rootCausalHash, shallowBranch)
(_localNamesOnly, ppe) <- Backend.scopedNamesForBranchHash codebase (Just rootCausal) namespacePath
renderedDoc <-
Backend.findDocInBranchAndRender
docNames
width
runtime
codebase
ppe
shallowBranch
pure renderedDoc
findAndRenderDoc docNames runtime codebase namespacePath rootCausalHash _mayWidth = do
(shallowBranchAtNamespace, namesPerspective) <-
liftIO . (Codebase.runTransaction codebase) $ do
rootCausal <- Backend.resolveCausalHashV2 (Just rootCausalHash)
let rootBranchHash = V2Causal.valueHash rootCausal
namespaceCausal <- Codebase.getShallowCausalAtPath namespacePath (Just rootCausal)
shallowBranchAtNamespace <- V2Causal.value namespaceCausal
namesPerspective <- Ops.namesPerspectiveForRootAndPath rootBranchHash (coerce . Path.toList $ namespacePath)
pure (shallowBranchAtNamespace, namesPerspective)
let mayDocRef = Backend.findDocInBranch docNames shallowBranchAtNamespace
for mayDocRef \docRef -> do
eDoc <- liftIO $ evalDocRef runtime codebase docRef
let docDeps = Doc.dependencies eDoc <> Set.singleton (LD.TermReference docRef)
docPPE <- liftIO $ Codebase.runTransaction codebase $ PPESqlite.ppedForReferences namesPerspective docDeps
pure $ Doc.renderDoc docPPE eDoc

View File

@ -39,6 +39,7 @@ import Unison.Codebase.Branch qualified as Branch
import Unison.Codebase.Editor.DisplayObject
( DisplayObject,
)
import Unison.Codebase.Path qualified as Path
import Unison.Hash qualified as Hash
import Unison.HashQualified qualified as HQ
import Unison.HashQualified' qualified as HQ'
@ -71,6 +72,34 @@ type UnisonName = Text
type UnisonHash = Text
data NamespaceDetails = NamespaceDetails
{ fqn :: Path.Path,
hash :: UnisonHash,
readme :: Maybe Doc
}
deriving (Generic, Show)
instance Docs.ToSample NamespaceDetails where
toSamples _ =
[ ( "When no value is provided for `namespace`, the root namespace `.` is "
<> "listed by default",
NamespaceDetails
Path.empty
"#gjlk0dna8dongct6lsd19d1o9hi5n642t8jttga5e81e91fviqjdffem0tlddj7ahodjo5"
Nothing
)
]
instance ToJSON NamespaceDetails where
toJSON NamespaceDetails {..} =
object
[ "fqn" .= fqn,
"hash" .= hash,
"readme" .= readme
]
deriving instance ToSchema NamespaceDetails
-- | A hash qualified name, unlike HashQualified, the hash is required
data ExactName name ref = ExactName
{ name :: name,

View File

@ -1,6 +1,6 @@
cabal-version: 1.12
-- This file has been generated from package.yaml by hpack version 0.35.1.
-- This file has been generated from package.yaml by hpack version 0.35.2.
--
-- see: https://github.com/sol/hpack
@ -42,6 +42,7 @@ library
Unison.Server.Share
Unison.Server.Share.Definitions
Unison.Server.Share.FuzzyFind
Unison.Server.Share.NamespaceDetails
Unison.Server.Share.RenderDoc
Unison.Server.Syntax
Unison.Server.Types
@ -80,6 +81,7 @@ library
TypeApplications
TypeOperators
ViewPatterns
ImportQualifiedPost
ghc-options: -Wall
build-depends:
NanoID

View File

@ -5,6 +5,6 @@ Thus, make sure the contents of this file define the contents of the cache
(e.g. don't pull `latest`.)
```ucm
.> pull @unison/base/releases/2.0.0 .base
.> pull @unison/base/releases/2.2.0 .base
.> compile.native.fetch
```

View File

@ -5,9 +5,9 @@ Thus, make sure the contents of this file define the contents of the cache
(e.g. don't pull `latest`.)
```ucm
.> pull @unison/base/releases/2.0.0 .base
.> pull @unison/base/releases/2.2.0 .base
Downloaded 11939 entities.
Downloaded 12209 entities.
@ -15,11 +15,11 @@ Thus, make sure the contents of this file define the contents of the cache
.> compile.native.fetch
Downloaded 65927 entities.
Downloaded 1255 entities.
Successfully updated .unison.internal from
unison.public.internal.trunk.
@unison/internal/releases/0.0.1.
```

View File

@ -64,6 +64,7 @@ to `Tests.check` and `Tests.checkEqual`).
```
```ucm:hide
.> alias.term ##IO.randomBytes IO.randomBytes
.> load unison-src/builtin-tests/io-tests.u
.> add
```

View File

@ -1,13 +1,18 @@
io.tests = Tests.main do
!test_getFileSize
!test_getFileSize_err
!test_getFileSize_err
!test_getFileTimestamp
!test_getFileTimestamp_err
!io.test.seek.absolute
!io.test.seek.relative
!io.test.getLine
!io.test.getsetBuffering
!io.test_getEcho
!io.test_getArgs
!io.test_getEnv
!io.test.getRandomBytes
!io.test_getSomeBytes
testFile = do
fp = FilePath ((FilePath.toText !getTempDirectory) ++ "/unison-test")
@ -47,7 +52,7 @@ test_getFileTimestamp = do
Tests.fail "File timestamp is too late" ((Int.toText ts) ++ " vs " ++ (Int.toText after))
else
Tests.pass "File timestamp is reasonable"
test_getFileTimestamp_err = do
expectError' "File timestamp of missing file" ["does not exist", "error getting"] '(FilePath.getTimestamp !testFile)
@ -104,3 +109,34 @@ io.test.getsetBuffering = do
checkEqual "Line" b2 LineBuffering
checkEqual "No" b3 NoBuffering
is_a_tty = do
exitCode = call "test" ["-t", "0"]
exitCode == 0
io.test_getEcho = do
if is_a_tty () then
prev = getEcho stdIn
setEcho stdIn false
checkEqual "echo turned off" (getEcho stdIn) false
setEcho stdIn true
checkEqual "echo turned back on" (getEcho stdIn) true
setEcho stdIn prev
else
()
io.test_getArgs = do
checkEqual "cli args" !getArgs []
io.test_getEnv = do
checkEqual "HOME env variable" (startsWith "/" (getEnv "HOME")) true
io.test.getRandomBytes = do
bs = IO.randomBytes 10
checkEqual "get 10 random bytes" 10 (base.Bytes.size bs)
io.test_getSomeBytes = do
fp = !testFile
_ = writeFile fp "one\ntwo\nthree"
h = open fp Read
one = getSomeBytes h 3
checkEqual "get some bytes" one (toUtf8 "one")

View File

@ -66,6 +66,7 @@ to `Tests.check` and `Tests.checkEqual`).
```
```ucm:hide
.> alias.term ##IO.randomBytes IO.randomBytes
.> load unison-src/builtin-tests/io-tests.u
.> add
```

View File

@ -26,6 +26,7 @@ Tests.main suite = do
Tests.run : '{IO,Exception,Tests} () ->{IO,Exception} Boolean
Tests.run suite =
use Nat +
h passed failed = cases
{ _ } -> (passed, failed)
{ pass msg -> k } ->

View File

@ -41,7 +41,7 @@ chainClient portPromise toSend =
tlsock = Tls.handshake tls
TlsSocket.send tlsock (toUtf8 toSend)
-- res = fromUtf8 (TlsSocket.receive tlsock)
TlsSocket.close tlsock
TlsSocket.terminate tlsock
-- res
-- server receives then sends
@ -61,7 +61,7 @@ chainServer portPromise toSend =
tlsock = net.Tls.handshake tls
res = fromUtf8 (TlsSocket.receive tlsock)
-- TlsSocket.send tlsock (toUtf8 toSend)
TlsSocket.close tlsock
TlsSocket.terminate tlsock
res
tlsChainTest = do

View File

@ -66,7 +66,7 @@ tls.example.com = do
conn = base.IO.net.Tls.handshake tls
TlsSocket.send conn (toUtf8 "GET /index.html HTTP/1.0\r\nHost: example.com\r\n\r\n")
response = TlsSocket.receive conn
TlsSocket.close conn
TlsSocket.terminate conn
contains "HTTP/1.0 200 OK" (fromUtf8 response)
testConnectSelfSigned = do
@ -123,7 +123,7 @@ serverThread portPromise toSend =
tls = Tls.newServer tlsconfig sock'
tlsock = net.Tls.handshake tls
TlsSocket.send tlsock (toUtf8 toSend)
TlsSocket.close tlsock
TlsSocket.terminate tlsock
testClient : Optional SignedCert -> Text -> Promise Nat -> '{IO} Either Failure Text
testClient cert hostname portVar _ = catch do

File diff suppressed because it is too large Load Diff

View File

@ -33,16 +33,16 @@ hasMetadata = 3
.dependencies> namespace.dependencies
External dependency Dependents in .dependencies
builtin.Int dependsOnInt
builtin.Int 1. dependsOnInt
builtin.Nat dependsOnIntAndNat
dependsOnNat
hasMetadata
builtin.Nat 2. dependsOnIntAndNat
3. dependsOnNat
4. hasMetadata
builtin.Text hasMetadata
builtin.Text 4. hasMetadata
builtin.Nat.drop dependsOnIntAndNat
builtin.Nat.drop 2. dependsOnIntAndNat
metadata.myMetadata hasMetadata
metadata.myMetadata 4. hasMetadata
```

View File

@ -337,364 +337,365 @@ Let's try it!
251. io2.IO.putBytes.impl : Handle
-> Bytes
->{IO} Either Failure ()
252. io2.IO.ready.impl : Handle ->{IO} Either Failure Boolean
253. io2.IO.ref : a ->{IO} Ref {IO} a
254. io2.IO.removeDirectory.impl : Text
252. io2.IO.randomBytes : Nat ->{IO} Bytes
253. io2.IO.ready.impl : Handle ->{IO} Either Failure Boolean
254. io2.IO.ref : a ->{IO} Ref {IO} a
255. io2.IO.removeDirectory.impl : Text
->{IO} Either Failure ()
255. io2.IO.removeFile.impl : Text ->{IO} Either Failure ()
256. io2.IO.renameDirectory.impl : Text
256. io2.IO.removeFile.impl : Text ->{IO} Either Failure ()
257. io2.IO.renameDirectory.impl : Text
-> Text
->{IO} Either Failure ()
257. io2.IO.renameFile.impl : Text
258. io2.IO.renameFile.impl : Text
-> Text
->{IO} Either Failure ()
258. io2.IO.seekHandle.impl : Handle
259. io2.IO.seekHandle.impl : Handle
-> SeekMode
-> Int
->{IO} Either Failure ()
259. io2.IO.serverSocket.impl : Optional Text
260. io2.IO.serverSocket.impl : Optional Text
-> Text
->{IO} Either Failure Socket
260. io2.IO.setBuffering.impl : Handle
261. io2.IO.setBuffering.impl : Handle
-> BufferMode
->{IO} Either Failure ()
261. io2.IO.setCurrentDirectory.impl : Text
262. io2.IO.setCurrentDirectory.impl : Text
->{IO} Either
Failure ()
262. io2.IO.setEcho.impl : Handle
263. io2.IO.setEcho.impl : Handle
-> Boolean
->{IO} Either Failure ()
263. io2.IO.socketAccept.impl : Socket
264. io2.IO.socketAccept.impl : Socket
->{IO} Either Failure Socket
264. io2.IO.socketPort.impl : Socket ->{IO} Either Failure Nat
265. io2.IO.socketReceive.impl : Socket
265. io2.IO.socketPort.impl : Socket ->{IO} Either Failure Nat
266. io2.IO.socketReceive.impl : Socket
-> Nat
->{IO} Either Failure Bytes
266. io2.IO.socketSend.impl : Socket
267. io2.IO.socketSend.impl : Socket
-> Bytes
->{IO} Either Failure ()
267. io2.IO.stdHandle : StdHandle -> Handle
268. io2.IO.systemTime.impl : '{IO} Either Failure Nat
269. io2.IO.systemTimeMicroseconds : '{IO} Int
270. io2.IO.tryEval : '{IO} a ->{IO, Exception} a
271. unique type io2.IOError
272. io2.IOError.AlreadyExists : IOError
273. io2.IOError.EOF : IOError
274. io2.IOError.IllegalOperation : IOError
275. io2.IOError.NoSuchThing : IOError
276. io2.IOError.PermissionDenied : IOError
277. io2.IOError.ResourceBusy : IOError
278. io2.IOError.ResourceExhausted : IOError
279. io2.IOError.UserError : IOError
280. unique type io2.IOFailure
281. unique type io2.MiscFailure
282. builtin type io2.MVar
283. io2.MVar.isEmpty : MVar a ->{IO} Boolean
284. io2.MVar.new : a ->{IO} MVar a
285. io2.MVar.newEmpty : '{IO} MVar a
286. io2.MVar.put.impl : MVar a -> a ->{IO} Either Failure ()
287. io2.MVar.read.impl : MVar a ->{IO} Either Failure a
288. io2.MVar.swap.impl : MVar a -> a ->{IO} Either Failure a
289. io2.MVar.take.impl : MVar a ->{IO} Either Failure a
290. io2.MVar.tryPut.impl : MVar a
268. io2.IO.stdHandle : StdHandle -> Handle
269. io2.IO.systemTime.impl : '{IO} Either Failure Nat
270. io2.IO.systemTimeMicroseconds : '{IO} Int
271. io2.IO.tryEval : '{IO} a ->{IO, Exception} a
272. unique type io2.IOError
273. io2.IOError.AlreadyExists : IOError
274. io2.IOError.EOF : IOError
275. io2.IOError.IllegalOperation : IOError
276. io2.IOError.NoSuchThing : IOError
277. io2.IOError.PermissionDenied : IOError
278. io2.IOError.ResourceBusy : IOError
279. io2.IOError.ResourceExhausted : IOError
280. io2.IOError.UserError : IOError
281. unique type io2.IOFailure
282. unique type io2.MiscFailure
283. builtin type io2.MVar
284. io2.MVar.isEmpty : MVar a ->{IO} Boolean
285. io2.MVar.new : a ->{IO} MVar a
286. io2.MVar.newEmpty : '{IO} MVar a
287. io2.MVar.put.impl : MVar a -> a ->{IO} Either Failure ()
288. io2.MVar.read.impl : MVar a ->{IO} Either Failure a
289. io2.MVar.swap.impl : MVar a -> a ->{IO} Either Failure a
290. io2.MVar.take.impl : MVar a ->{IO} Either Failure a
291. io2.MVar.tryPut.impl : MVar a
-> a
->{IO} Either Failure Boolean
291. io2.MVar.tryRead.impl : MVar a
292. io2.MVar.tryRead.impl : MVar a
->{IO} Either
Failure (Optional a)
292. io2.MVar.tryTake : MVar a ->{IO} Optional a
293. builtin type io2.ProcessHandle
294. builtin type io2.Promise
295. io2.Promise.new : '{IO} Promise a
296. io2.Promise.read : Promise a ->{IO} a
297. io2.Promise.tryRead : Promise a ->{IO} Optional a
298. io2.Promise.write : Promise a -> a ->{IO} Boolean
299. io2.Ref.cas : Ref {IO} a -> Ticket a -> a ->{IO} Boolean
300. io2.Ref.readForCas : Ref {IO} a ->{IO} Ticket a
301. builtin type io2.Ref.Ticket
302. io2.Ref.Ticket.read : Ticket a -> a
303. unique type io2.RuntimeFailure
304. unique type io2.SeekMode
305. io2.SeekMode.AbsoluteSeek : SeekMode
306. io2.SeekMode.RelativeSeek : SeekMode
307. io2.SeekMode.SeekFromEnd : SeekMode
308. builtin type io2.Socket
309. unique type io2.StdHandle
310. io2.StdHandle.StdErr : StdHandle
311. io2.StdHandle.StdIn : StdHandle
312. io2.StdHandle.StdOut : StdHandle
313. builtin type io2.STM
314. io2.STM.atomically : '{STM} a ->{IO} a
315. io2.STM.retry : '{STM} a
316. unique type io2.STMFailure
317. builtin type io2.ThreadId
318. unique type io2.ThreadKilledFailure
319. builtin type io2.Tls
320. builtin type io2.Tls.Cipher
321. builtin type io2.Tls.ClientConfig
322. io2.Tls.ClientConfig.certificates.set : [SignedCert]
293. io2.MVar.tryTake : MVar a ->{IO} Optional a
294. builtin type io2.ProcessHandle
295. builtin type io2.Promise
296. io2.Promise.new : '{IO} Promise a
297. io2.Promise.read : Promise a ->{IO} a
298. io2.Promise.tryRead : Promise a ->{IO} Optional a
299. io2.Promise.write : Promise a -> a ->{IO} Boolean
300. io2.Ref.cas : Ref {IO} a -> Ticket a -> a ->{IO} Boolean
301. io2.Ref.readForCas : Ref {IO} a ->{IO} Ticket a
302. builtin type io2.Ref.Ticket
303. io2.Ref.Ticket.read : Ticket a -> a
304. unique type io2.RuntimeFailure
305. unique type io2.SeekMode
306. io2.SeekMode.AbsoluteSeek : SeekMode
307. io2.SeekMode.RelativeSeek : SeekMode
308. io2.SeekMode.SeekFromEnd : SeekMode
309. builtin type io2.Socket
310. unique type io2.StdHandle
311. io2.StdHandle.StdErr : StdHandle
312. io2.StdHandle.StdIn : StdHandle
313. io2.StdHandle.StdOut : StdHandle
314. builtin type io2.STM
315. io2.STM.atomically : '{STM} a ->{IO} a
316. io2.STM.retry : '{STM} a
317. unique type io2.STMFailure
318. builtin type io2.ThreadId
319. unique type io2.ThreadKilledFailure
320. builtin type io2.Tls
321. builtin type io2.Tls.Cipher
322. builtin type io2.Tls.ClientConfig
323. io2.Tls.ClientConfig.certificates.set : [SignedCert]
-> ClientConfig
-> ClientConfig
323. io2.TLS.ClientConfig.ciphers.set : [Cipher]
324. io2.TLS.ClientConfig.ciphers.set : [Cipher]
-> ClientConfig
-> ClientConfig
324. io2.Tls.ClientConfig.default : Text
325. io2.Tls.ClientConfig.default : Text
-> Bytes
-> ClientConfig
325. io2.Tls.ClientConfig.versions.set : [Version]
326. io2.Tls.ClientConfig.versions.set : [Version]
-> ClientConfig
-> ClientConfig
326. io2.Tls.decodeCert.impl : Bytes
327. io2.Tls.decodeCert.impl : Bytes
-> Either Failure SignedCert
327. io2.Tls.decodePrivateKey : Bytes -> [PrivateKey]
328. io2.Tls.encodeCert : SignedCert -> Bytes
329. io2.Tls.encodePrivateKey : PrivateKey -> Bytes
330. io2.Tls.handshake.impl : Tls ->{IO} Either Failure ()
331. io2.Tls.newClient.impl : ClientConfig
328. io2.Tls.decodePrivateKey : Bytes -> [PrivateKey]
329. io2.Tls.encodeCert : SignedCert -> Bytes
330. io2.Tls.encodePrivateKey : PrivateKey -> Bytes
331. io2.Tls.handshake.impl : Tls ->{IO} Either Failure ()
332. io2.Tls.newClient.impl : ClientConfig
-> Socket
->{IO} Either Failure Tls
332. io2.Tls.newServer.impl : ServerConfig
333. io2.Tls.newServer.impl : ServerConfig
-> Socket
->{IO} Either Failure Tls
333. builtin type io2.Tls.PrivateKey
334. io2.Tls.receive.impl : Tls ->{IO} Either Failure Bytes
335. io2.Tls.send.impl : Tls -> Bytes ->{IO} Either Failure ()
336. builtin type io2.Tls.ServerConfig
337. io2.Tls.ServerConfig.certificates.set : [SignedCert]
334. builtin type io2.Tls.PrivateKey
335. io2.Tls.receive.impl : Tls ->{IO} Either Failure Bytes
336. io2.Tls.send.impl : Tls -> Bytes ->{IO} Either Failure ()
337. builtin type io2.Tls.ServerConfig
338. io2.Tls.ServerConfig.certificates.set : [SignedCert]
-> ServerConfig
-> ServerConfig
338. io2.Tls.ServerConfig.ciphers.set : [Cipher]
339. io2.Tls.ServerConfig.ciphers.set : [Cipher]
-> ServerConfig
-> ServerConfig
339. io2.Tls.ServerConfig.default : [SignedCert]
340. io2.Tls.ServerConfig.default : [SignedCert]
-> PrivateKey
-> ServerConfig
340. io2.Tls.ServerConfig.versions.set : [Version]
341. io2.Tls.ServerConfig.versions.set : [Version]
-> ServerConfig
-> ServerConfig
341. builtin type io2.Tls.SignedCert
342. io2.Tls.terminate.impl : Tls ->{IO} Either Failure ()
343. builtin type io2.Tls.Version
344. unique type io2.TlsFailure
345. builtin type io2.TVar
346. io2.TVar.new : a ->{STM} TVar a
347. io2.TVar.newIO : a ->{IO} TVar a
348. io2.TVar.read : TVar a ->{STM} a
349. io2.TVar.readIO : TVar a ->{IO} a
350. io2.TVar.swap : TVar a -> a ->{STM} a
351. io2.TVar.write : TVar a -> a ->{STM} ()
352. io2.validateSandboxed : [Term] -> a -> Boolean
353. unique type IsPropagated
354. IsPropagated.IsPropagated : IsPropagated
355. unique type IsTest
356. IsTest.IsTest : IsTest
357. unique type Link
358. builtin type Link.Term
359. Link.Term : Term -> Link
360. Link.Term.toText : Term -> Text
361. builtin type Link.Type
362. Link.Type : Type -> Link
363. builtin type List
364. List.++ : [a] -> [a] -> [a]
365. List.+: : a -> [a] -> [a]
366. List.:+ : [a] -> a -> [a]
367. List.at : Nat -> [a] -> Optional a
368. List.cons : a -> [a] -> [a]
369. List.drop : Nat -> [a] -> [a]
370. List.empty : [a]
371. List.size : [a] -> Nat
372. List.snoc : [a] -> a -> [a]
373. List.take : Nat -> [a] -> [a]
374. metadata.isPropagated : IsPropagated
375. metadata.isTest : IsTest
376. builtin type MutableArray
377. MutableArray.copyTo! : MutableArray g a
342. builtin type io2.Tls.SignedCert
343. io2.Tls.terminate.impl : Tls ->{IO} Either Failure ()
344. builtin type io2.Tls.Version
345. unique type io2.TlsFailure
346. builtin type io2.TVar
347. io2.TVar.new : a ->{STM} TVar a
348. io2.TVar.newIO : a ->{IO} TVar a
349. io2.TVar.read : TVar a ->{STM} a
350. io2.TVar.readIO : TVar a ->{IO} a
351. io2.TVar.swap : TVar a -> a ->{STM} a
352. io2.TVar.write : TVar a -> a ->{STM} ()
353. io2.validateSandboxed : [Term] -> a -> Boolean
354. unique type IsPropagated
355. IsPropagated.IsPropagated : IsPropagated
356. unique type IsTest
357. IsTest.IsTest : IsTest
358. unique type Link
359. builtin type Link.Term
360. Link.Term : Term -> Link
361. Link.Term.toText : Term -> Text
362. builtin type Link.Type
363. Link.Type : Type -> Link
364. builtin type List
365. List.++ : [a] -> [a] -> [a]
366. List.+: : a -> [a] -> [a]
367. List.:+ : [a] -> a -> [a]
368. List.at : Nat -> [a] -> Optional a
369. List.cons : a -> [a] -> [a]
370. List.drop : Nat -> [a] -> [a]
371. List.empty : [a]
372. List.size : [a] -> Nat
373. List.snoc : [a] -> a -> [a]
374. List.take : Nat -> [a] -> [a]
375. metadata.isPropagated : IsPropagated
376. metadata.isTest : IsTest
377. builtin type MutableArray
378. MutableArray.copyTo! : MutableArray g a
-> Nat
-> MutableArray g a
-> Nat
-> Nat
->{g, Exception} ()
378. MutableArray.freeze : MutableArray g a
379. MutableArray.freeze : MutableArray g a
-> Nat
-> Nat
->{g} ImmutableArray a
379. MutableArray.freeze! : MutableArray g a
380. MutableArray.freeze! : MutableArray g a
->{g} ImmutableArray a
380. MutableArray.read : MutableArray g a
381. MutableArray.read : MutableArray g a
-> Nat
->{g, Exception} a
381. MutableArray.size : MutableArray g a -> Nat
382. MutableArray.write : MutableArray g a
382. MutableArray.size : MutableArray g a -> Nat
383. MutableArray.write : MutableArray g a
-> Nat
-> a
->{g, Exception} ()
383. builtin type MutableByteArray
384. MutableByteArray.copyTo! : MutableByteArray g
384. builtin type MutableByteArray
385. MutableByteArray.copyTo! : MutableByteArray g
-> Nat
-> MutableByteArray g
-> Nat
-> Nat
->{g, Exception} ()
385. MutableByteArray.freeze : MutableByteArray g
386. MutableByteArray.freeze : MutableByteArray g
-> Nat
-> Nat
->{g} ImmutableByteArray
386. MutableByteArray.freeze! : MutableByteArray g
387. MutableByteArray.freeze! : MutableByteArray g
->{g} ImmutableByteArray
387. MutableByteArray.read16be : MutableByteArray g
388. MutableByteArray.read16be : MutableByteArray g
-> Nat
->{g, Exception} Nat
388. MutableByteArray.read24be : MutableByteArray g
389. MutableByteArray.read24be : MutableByteArray g
-> Nat
->{g, Exception} Nat
389. MutableByteArray.read32be : MutableByteArray g
390. MutableByteArray.read32be : MutableByteArray g
-> Nat
->{g, Exception} Nat
390. MutableByteArray.read40be : MutableByteArray g
391. MutableByteArray.read40be : MutableByteArray g
-> Nat
->{g, Exception} Nat
391. MutableByteArray.read64be : MutableByteArray g
392. MutableByteArray.read64be : MutableByteArray g
-> Nat
->{g, Exception} Nat
392. MutableByteArray.read8 : MutableByteArray g
393. MutableByteArray.read8 : MutableByteArray g
-> Nat
->{g, Exception} Nat
393. MutableByteArray.size : MutableByteArray g -> Nat
394. MutableByteArray.write16be : MutableByteArray g
394. MutableByteArray.size : MutableByteArray g -> Nat
395. MutableByteArray.write16be : MutableByteArray g
-> Nat
-> Nat
->{g, Exception} ()
395. MutableByteArray.write32be : MutableByteArray g
396. MutableByteArray.write32be : MutableByteArray g
-> Nat
-> Nat
->{g, Exception} ()
396. MutableByteArray.write64be : MutableByteArray g
397. MutableByteArray.write64be : MutableByteArray g
-> Nat
-> Nat
->{g, Exception} ()
397. MutableByteArray.write8 : MutableByteArray g
398. MutableByteArray.write8 : MutableByteArray g
-> Nat
-> Nat
->{g, Exception} ()
398. builtin type Nat
399. Nat.* : Nat -> Nat -> Nat
400. Nat.+ : Nat -> Nat -> Nat
401. Nat./ : Nat -> Nat -> Nat
402. Nat.and : Nat -> Nat -> Nat
403. Nat.complement : Nat -> Nat
404. Nat.drop : Nat -> Nat -> Nat
405. Nat.eq : Nat -> Nat -> Boolean
406. Nat.fromText : Text -> Optional Nat
407. Nat.gt : Nat -> Nat -> Boolean
408. Nat.gteq : Nat -> Nat -> Boolean
409. Nat.increment : Nat -> Nat
410. Nat.isEven : Nat -> Boolean
411. Nat.isOdd : Nat -> Boolean
412. Nat.leadingZeros : Nat -> Nat
413. Nat.lt : Nat -> Nat -> Boolean
414. Nat.lteq : Nat -> Nat -> Boolean
415. Nat.mod : Nat -> Nat -> Nat
416. Nat.or : Nat -> Nat -> Nat
417. Nat.popCount : Nat -> Nat
418. Nat.pow : Nat -> Nat -> Nat
419. Nat.shiftLeft : Nat -> Nat -> Nat
420. Nat.shiftRight : Nat -> Nat -> Nat
421. Nat.sub : Nat -> Nat -> Int
422. Nat.toFloat : Nat -> Float
423. Nat.toInt : Nat -> Int
424. Nat.toText : Nat -> Text
425. Nat.trailingZeros : Nat -> Nat
426. Nat.xor : Nat -> Nat -> Nat
427. structural type Optional a
428. Optional.None : Optional a
429. Optional.Some : a -> Optional a
430. builtin type Pattern
431. Pattern.capture : Pattern a -> Pattern a
432. Pattern.isMatch : Pattern a -> a -> Boolean
433. Pattern.join : [Pattern a] -> Pattern a
434. Pattern.many : Pattern a -> Pattern a
435. Pattern.or : Pattern a -> Pattern a -> Pattern a
436. Pattern.replicate : Nat -> Nat -> Pattern a -> Pattern a
437. Pattern.run : Pattern a -> a -> Optional ([a], a)
438. builtin type Ref
439. Ref.read : Ref g a ->{g} a
440. Ref.write : Ref g a -> a ->{g} ()
441. builtin type Request
442. builtin type Scope
443. Scope.array : Nat ->{Scope s} MutableArray (Scope s) a
444. Scope.arrayOf : a
399. builtin type Nat
400. Nat.* : Nat -> Nat -> Nat
401. Nat.+ : Nat -> Nat -> Nat
402. Nat./ : Nat -> Nat -> Nat
403. Nat.and : Nat -> Nat -> Nat
404. Nat.complement : Nat -> Nat
405. Nat.drop : Nat -> Nat -> Nat
406. Nat.eq : Nat -> Nat -> Boolean
407. Nat.fromText : Text -> Optional Nat
408. Nat.gt : Nat -> Nat -> Boolean
409. Nat.gteq : Nat -> Nat -> Boolean
410. Nat.increment : Nat -> Nat
411. Nat.isEven : Nat -> Boolean
412. Nat.isOdd : Nat -> Boolean
413. Nat.leadingZeros : Nat -> Nat
414. Nat.lt : Nat -> Nat -> Boolean
415. Nat.lteq : Nat -> Nat -> Boolean
416. Nat.mod : Nat -> Nat -> Nat
417. Nat.or : Nat -> Nat -> Nat
418. Nat.popCount : Nat -> Nat
419. Nat.pow : Nat -> Nat -> Nat
420. Nat.shiftLeft : Nat -> Nat -> Nat
421. Nat.shiftRight : Nat -> Nat -> Nat
422. Nat.sub : Nat -> Nat -> Int
423. Nat.toFloat : Nat -> Float
424. Nat.toInt : Nat -> Int
425. Nat.toText : Nat -> Text
426. Nat.trailingZeros : Nat -> Nat
427. Nat.xor : Nat -> Nat -> Nat
428. structural type Optional a
429. Optional.None : Optional a
430. Optional.Some : a -> Optional a
431. builtin type Pattern
432. Pattern.capture : Pattern a -> Pattern a
433. Pattern.isMatch : Pattern a -> a -> Boolean
434. Pattern.join : [Pattern a] -> Pattern a
435. Pattern.many : Pattern a -> Pattern a
436. Pattern.or : Pattern a -> Pattern a -> Pattern a
437. Pattern.replicate : Nat -> Nat -> Pattern a -> Pattern a
438. Pattern.run : Pattern a -> a -> Optional ([a], a)
439. builtin type Ref
440. Ref.read : Ref g a ->{g} a
441. Ref.write : Ref g a -> a ->{g} ()
442. builtin type Request
443. builtin type Scope
444. Scope.array : Nat ->{Scope s} MutableArray (Scope s) a
445. Scope.arrayOf : a
-> Nat
->{Scope s} MutableArray (Scope s) a
445. Scope.bytearray : Nat
446. Scope.bytearray : Nat
->{Scope s} MutableByteArray (Scope s)
446. Scope.bytearrayOf : Nat
447. Scope.bytearrayOf : Nat
-> Nat
->{Scope s} MutableByteArray
(Scope s)
447. Scope.ref : a ->{Scope s} Ref {Scope s} a
448. Scope.run : (∀ s. '{g, Scope s} r) ->{g} r
449. structural type SeqView a b
450. SeqView.VElem : a -> b -> SeqView a b
451. SeqView.VEmpty : SeqView a b
452. Socket.toText : Socket -> Text
453. unique type Test.Result
454. Test.Result.Fail : Text -> Result
455. Test.Result.Ok : Text -> Result
456. builtin type Text
457. Text.!= : Text -> Text -> Boolean
458. Text.++ : Text -> Text -> Text
459. Text.drop : Nat -> Text -> Text
460. Text.empty : Text
461. Text.eq : Text -> Text -> Boolean
462. Text.fromCharList : [Char] -> Text
463. Text.fromUtf8.impl : Bytes -> Either Failure Text
464. Text.gt : Text -> Text -> Boolean
465. Text.gteq : Text -> Text -> Boolean
466. Text.indexOf : Text -> Text -> Optional Nat
467. Text.lt : Text -> Text -> Boolean
468. Text.lteq : Text -> Text -> Boolean
469. Text.patterns.anyChar : Pattern Text
470. Text.patterns.char : Class -> Pattern Text
471. Text.patterns.charIn : [Char] -> Pattern Text
472. Text.patterns.charRange : Char -> Char -> Pattern Text
473. Text.patterns.digit : Pattern Text
474. Text.patterns.eof : Pattern Text
475. Text.patterns.letter : Pattern Text
476. Text.patterns.literal : Text -> Pattern Text
477. Text.patterns.notCharIn : [Char] -> Pattern Text
478. Text.patterns.notCharRange : Char -> Char -> Pattern Text
479. Text.patterns.punctuation : Pattern Text
480. Text.patterns.space : Pattern Text
481. Text.repeat : Nat -> Text -> Text
482. Text.reverse : Text -> Text
483. Text.size : Text -> Nat
484. Text.take : Nat -> Text -> Text
485. Text.toCharList : Text -> [Char]
486. Text.toLowercase : Text -> Text
487. Text.toUppercase : Text -> Text
488. Text.toUtf8 : Text -> Bytes
489. Text.uncons : Text -> Optional (Char, Text)
490. Text.unsnoc : Text -> Optional (Text, Char)
491. ThreadId.toText : ThreadId -> Text
492. todo : a -> b
493. structural type Tuple a b
494. Tuple.Cons : a -> b -> Tuple a b
495. structural type Unit
496. Unit.Unit : ()
497. Universal.< : a -> a -> Boolean
498. Universal.<= : a -> a -> Boolean
499. Universal.== : a -> a -> Boolean
500. Universal.> : a -> a -> Boolean
501. Universal.>= : a -> a -> Boolean
502. Universal.compare : a -> a -> Int
503. Universal.murmurHash : a -> Nat
504. unsafe.coerceAbilities : (a ->{e1} b) -> a ->{e2} b
505. builtin type Value
506. Value.dependencies : Value -> [Term]
507. Value.deserialize : Bytes -> Either Text Value
508. Value.load : Value ->{IO} Either [Term] a
509. Value.serialize : Value -> Bytes
510. Value.value : a -> Value
448. Scope.ref : a ->{Scope s} Ref {Scope s} a
449. Scope.run : (∀ s. '{g, Scope s} r) ->{g} r
450. structural type SeqView a b
451. SeqView.VElem : a -> b -> SeqView a b
452. SeqView.VEmpty : SeqView a b
453. Socket.toText : Socket -> Text
454. unique type Test.Result
455. Test.Result.Fail : Text -> Result
456. Test.Result.Ok : Text -> Result
457. builtin type Text
458. Text.!= : Text -> Text -> Boolean
459. Text.++ : Text -> Text -> Text
460. Text.drop : Nat -> Text -> Text
461. Text.empty : Text
462. Text.eq : Text -> Text -> Boolean
463. Text.fromCharList : [Char] -> Text
464. Text.fromUtf8.impl : Bytes -> Either Failure Text
465. Text.gt : Text -> Text -> Boolean
466. Text.gteq : Text -> Text -> Boolean
467. Text.indexOf : Text -> Text -> Optional Nat
468. Text.lt : Text -> Text -> Boolean
469. Text.lteq : Text -> Text -> Boolean
470. Text.patterns.anyChar : Pattern Text
471. Text.patterns.char : Class -> Pattern Text
472. Text.patterns.charIn : [Char] -> Pattern Text
473. Text.patterns.charRange : Char -> Char -> Pattern Text
474. Text.patterns.digit : Pattern Text
475. Text.patterns.eof : Pattern Text
476. Text.patterns.letter : Pattern Text
477. Text.patterns.literal : Text -> Pattern Text
478. Text.patterns.notCharIn : [Char] -> Pattern Text
479. Text.patterns.notCharRange : Char -> Char -> Pattern Text
480. Text.patterns.punctuation : Pattern Text
481. Text.patterns.space : Pattern Text
482. Text.repeat : Nat -> Text -> Text
483. Text.reverse : Text -> Text
484. Text.size : Text -> Nat
485. Text.take : Nat -> Text -> Text
486. Text.toCharList : Text -> [Char]
487. Text.toLowercase : Text -> Text
488. Text.toUppercase : Text -> Text
489. Text.toUtf8 : Text -> Bytes
490. Text.uncons : Text -> Optional (Char, Text)
491. Text.unsnoc : Text -> Optional (Text, Char)
492. ThreadId.toText : ThreadId -> Text
493. todo : a -> b
494. structural type Tuple a b
495. Tuple.Cons : a -> b -> Tuple a b
496. structural type Unit
497. Unit.Unit : ()
498. Universal.< : a -> a -> Boolean
499. Universal.<= : a -> a -> Boolean
500. Universal.== : a -> a -> Boolean
501. Universal.> : a -> a -> Boolean
502. Universal.>= : a -> a -> Boolean
503. Universal.compare : a -> a -> Int
504. Universal.murmurHash : a -> Nat
505. unsafe.coerceAbilities : (a ->{e1} b) -> a ->{e2} b
506. builtin type Value
507. Value.dependencies : Value -> [Term]
508. Value.deserialize : Bytes -> Either Text Value
509. Value.load : Value ->{IO} Either [Term] a
510. Value.serialize : Value -> Bytes
511. Value.value : a -> Value
.builtin> alias.many 94-104 .mylib

View File

@ -2,8 +2,8 @@ The `branch` command creates a new branch.
```ucm:hide
.> builtins.merge
.> project.create foo
.> project.create bar
.> project.create-empty foo
.> project.create-empty bar
```
First, we'll just create a loose code namespace with a term in it for later.

View File

@ -74,7 +74,7 @@ The `builtins.merge` command adds the known builtins to a `builtin` subnamespace
63. Value/ (5 terms)
64. bug (a -> b)
65. crypto/ (13 terms, 1 type)
66. io2/ (132 terms, 32 types)
66. io2/ (133 terms, 32 types)
67. metadata/ (2 terms)
68. todo (a -> b)
69. unsafe/ (1 term)

View File

@ -2,7 +2,7 @@ Deleting the branch you are on takes you to its parent (though this is impossibl
your working directory with each command).
```ucm
.> project.create foo
.> project.create-empty foo
foo/main> branch topic
foo/topic> delete.branch /topic
```

View File

@ -2,10 +2,22 @@ Deleting the branch you are on takes you to its parent (though this is impossibl
your working directory with each command).
```ucm
.> project.create foo
.> project.create-empty foo
🎉 I've created the project foo.
🎨 Type `ui` to explore this project's code in your browser.
🔭 Discover libraries at https://share.unison-lang.org
📖 Use `help-topic projects` to learn more about projects.
Write your first Unison code with UCM:
1. Open scratch.u.
2. Write some Unison code and save the file.
3. In UCM, type `add` to save it to your new project.
🎉 🥳 Happy coding!
foo/main> branch topic
Done. I've created the topic branch based off of main.

View File

@ -1,8 +1,8 @@
# delete.project
```ucm
.> project.create foo
.> project.create bar
.> project.create-empty foo
.> project.create-empty bar
.> projects
foo/main> delete.project foo
.> projects

View File

@ -1,16 +1,40 @@
# delete.project
```ucm
.> project.create foo
.> project.create-empty foo
🎉 I've created the project foo.
🎨 Type `ui` to explore this project's code in your browser.
🔭 Discover libraries at https://share.unison-lang.org
📖 Use `help-topic projects` to learn more about projects.
Write your first Unison code with UCM:
1. Open scratch.u.
2. Write some Unison code and save the file.
3. In UCM, type `add` to save it to your new project.
🎉 🥳 Happy coding!
☝️ The namespace . is empty.
.> project.create bar
.> project.create-empty bar
🎉 I've created the project bar.
🎨 Type `ui` to explore this project's code in your browser.
🔭 Discover libraries at https://share.unison-lang.org
📖 Use `help-topic projects` to learn more about projects.
Write your first Unison code with UCM:
1. Open scratch.u.
2. Write some Unison code and save the file.
3. In UCM, type `add` to save it to your new project.
🎉 🥳 Happy coding!
☝️ The namespace . is empty.
.> projects

View File

@ -23,7 +23,7 @@ Technically, the definitions all exist, but they have no names. `builtins.merge`
.foo> ls
1. builtin/ (444 terms, 66 types)
1. builtin/ (445 terms, 66 types)
```
And for a limited time, you can get even more builtin goodies:
@ -35,7 +35,7 @@ And for a limited time, you can get even more builtin goodies:
.foo> ls
1. builtin/ (616 terms, 84 types)
1. builtin/ (617 terms, 84 types)
```
More typically, you'd start out by pulling `base.

View File

@ -407,3 +407,19 @@ testTimeZone = do
.> run testTimeZone
```
### Get some random bytes
```unison:hide
testRandom : '{io2.IO} [Result]
testRandom = do
test = do
bytes = IO.randomBytes 10
check "randomBytes returns the right number of bytes" (size bytes == 10)
runTest test
```
```ucm
.> add
.> io.test testGetEnv
```

View File

@ -675,3 +675,33 @@ testTimeZone = do
()
```
### Get some random bytes
```unison
testRandom : '{io2.IO} [Result]
testRandom = do
test = do
bytes = IO.randomBytes 10
check "randomBytes returns the right number of bytes" (size bytes == 10)
runTest test
```
```ucm
.> add
⍟ I've added these definitions:
testRandom : '{IO} [Result]
.> io.test testGetEnv
New test results:
◉ testGetEnv PATH environent variable should be set
◉ testGetEnv DOESNTEXIST didn't exist
✅ 2 test(s) passing
Tip: Use view testGetEnv to view the source of a test.
```

View File

@ -115,13 +115,13 @@ it's still in the `history` of the parent namespace and can be resurrected at an
Note: The most recent namespace hash is immediately below this
message.
⊙ 1. #f6od7ck7o9
⊙ 1. #sch8ktifgr
- Deletes:
feature1.y
⊙ 2. #heefa0k2c2
⊙ 2. #ith4t7v4vg
+ Adds / updates:
@ -132,26 +132,26 @@ it's still in the `history` of the parent namespace and can be resurrected at an
Original name New name(s)
feature1.y master.y
⊙ 3. #ch6bfhmr01
⊙ 3. #n3o88a18gi
+ Adds / updates:
feature1.y
⊙ 4. #ipqe7pdtbj
⊙ 4. #6ko53cmfad
> Moves:
Original name New name
x master.x
⊙ 5. #aupi617lfm
⊙ 5. #ciekcpjcg5
+ Adds / updates:
x
□ 6. #okls7hes5i (start of history)
□ 6. #iog2oo140r (start of history)
```
To resurrect an old version of a namespace, you can learn its hash via the `history` command, then use `fork #namespacehash .newname`.

View File

@ -269,7 +269,7 @@ I should be able to move the root into a sub-namespace
.> ls
1. root/ (621 terms, 85 types)
1. root/ (622 terms, 85 types)
.> history
@ -278,13 +278,13 @@ I should be able to move the root into a sub-namespace
□ 1. #d7nl0iepe9 (start of history)
□ 1. #u3bfr3r2ei (start of history)
```
```ucm
.> ls .root.at.path
1. builtin/ (616 terms, 84 types)
1. builtin/ (617 terms, 84 types)
2. existing/ (1 term)
3. happy/ (3 terms, 1 type)
4. history/ (1 term)
@ -294,7 +294,7 @@ I should be able to move the root into a sub-namespace
Note: The most recent namespace hash is immediately below this
message.
⊙ 1. #u5b78mseu5
⊙ 1. #kq43nl1o04
- Deletes:
@ -305,7 +305,7 @@ I should be able to move the root into a sub-namespace
Original name New name
existing.a.termInA existing.b.termInA
⊙ 2. #l536jkmva1
⊙ 2. #4ukqhpj1ih
+ Adds / updates:
@ -317,26 +317,26 @@ I should be able to move the root into a sub-namespace
happy.b.termInA existing.a.termInA
history.b.termInA existing.a.termInA
⊙ 3. #l3c9t29mk5
⊙ 3. #l2cn17ai85
+ Adds / updates:
existing.a.termInA existing.b.termInB
⊙ 4. #6qe2efg2ej
⊙ 4. #3rbcejtv6q
> Moves:
Original name New name
history.a.termInA history.b.termInA
⊙ 5. #n2pk3n46k1
⊙ 5. #3ne1c2n3ob
- Deletes:
history.b.termInB
⊙ 6. #fe8hescuc7
⊙ 6. #ui7k5bt5gd
+ Adds / updates:
@ -347,13 +347,13 @@ I should be able to move the root into a sub-namespace
Original name New name(s)
happy.b.termInA history.a.termInA
⊙ 7. #902pi46cvk
⊙ 7. #5mcatjsb8n
+ Adds / updates:
history.a.termInA history.b.termInB
⊙ 8. #h3soer49rq
⊙ 8. #74jssi3o9e
> Moves:
@ -363,7 +363,7 @@ I should be able to move the root into a sub-namespace
happy.a.T.T2 happy.b.T.T2
happy.a.termInA happy.b.termInA
⊙ 9. #gn938smc7r
⊙ 9. #esu945u1md
+ Adds / updates:
@ -373,7 +373,7 @@ I should be able to move the root into a sub-namespace
happy.a.T.T
⊙ 10. #nkng97evbr
⊙ 10. #cas2jteq60
+ Adds / updates:
@ -385,7 +385,7 @@ I should be able to move the root into a sub-namespace
⊙ 11. #8pvqa03j6q
⊙ 11. #gdutne6b5t
```

View File

@ -1106,361 +1106,363 @@ d = c + 10
394. builtin.Char.Class.punctuation : Class
395. builtin.Text.patterns.punctuation : Pattern
Text
396. builtin.Char.Class.range : Char
396. builtin.io2.IO.randomBytes : Nat
->{IO} Bytes
397. builtin.Char.Class.range : Char
-> Char
-> Class
397. builtin.ImmutableArray.read : ImmutableArray
398. builtin.ImmutableArray.read : ImmutableArray
a
-> Nat
->{Exception} a
398. builtin.MutableArray.read : MutableArray
399. builtin.MutableArray.read : MutableArray
g a
-> Nat
->{g,
Exception} a
399. builtin.io2.Promise.read : Promise
400. builtin.io2.Promise.read : Promise
a
->{IO} a
400. builtin.Ref.read : Ref g a
401. builtin.Ref.read : Ref g a
->{g} a
401. builtin.io2.TVar.read : TVar a
402. builtin.io2.TVar.read : TVar a
->{STM} a
402. builtin.io2.Ref.Ticket.read : Ticket
403. builtin.io2.Ref.Ticket.read : Ticket
a
-> a
403. builtin.ImmutableByteArray.read16be : ImmutableByteArray
404. builtin.ImmutableByteArray.read16be : ImmutableByteArray
-> Nat
->{Exception} Nat
404. builtin.MutableByteArray.read16be : MutableByteArray
405. builtin.MutableByteArray.read16be : MutableByteArray
g
-> Nat
->{g,
Exception} Nat
405. builtin.ImmutableByteArray.read24be : ImmutableByteArray
406. builtin.ImmutableByteArray.read24be : ImmutableByteArray
-> Nat
->{Exception} Nat
406. builtin.MutableByteArray.read24be : MutableByteArray
407. builtin.MutableByteArray.read24be : MutableByteArray
g
-> Nat
->{g,
Exception} Nat
407. builtin.ImmutableByteArray.read32be : ImmutableByteArray
408. builtin.ImmutableByteArray.read32be : ImmutableByteArray
-> Nat
->{Exception} Nat
408. builtin.MutableByteArray.read32be : MutableByteArray
409. builtin.MutableByteArray.read32be : MutableByteArray
g
-> Nat
->{g,
Exception} Nat
409. builtin.ImmutableByteArray.read40be : ImmutableByteArray
410. builtin.ImmutableByteArray.read40be : ImmutableByteArray
-> Nat
->{Exception} Nat
410. builtin.MutableByteArray.read40be : MutableByteArray
411. builtin.MutableByteArray.read40be : MutableByteArray
g
-> Nat
->{g,
Exception} Nat
411. builtin.ImmutableByteArray.read64be : ImmutableByteArray
412. builtin.ImmutableByteArray.read64be : ImmutableByteArray
-> Nat
->{Exception} Nat
412. builtin.MutableByteArray.read64be : MutableByteArray
413. builtin.MutableByteArray.read64be : MutableByteArray
g
-> Nat
->{g,
Exception} Nat
413. builtin.ImmutableByteArray.read8 : ImmutableByteArray
414. builtin.ImmutableByteArray.read8 : ImmutableByteArray
-> Nat
->{Exception} Nat
414. builtin.MutableByteArray.read8 : MutableByteArray
415. builtin.MutableByteArray.read8 : MutableByteArray
g
-> Nat
->{g,
Exception} Nat
415. builtin.io2.Ref.readForCas : Ref
416. builtin.io2.Ref.readForCas : Ref
{IO} a
->{IO} Ticket
a
416. builtin.io2.TVar.readIO : TVar a
417. builtin.io2.TVar.readIO : TVar a
->{IO} a
417. builtin.io2.Clock.internals.realtime : '{IO} Either
418. builtin.io2.Clock.internals.realtime : '{IO} Either
Failure
TimeSpec
418. builtin.io2.IO.ref : a
419. builtin.io2.IO.ref : a
->{IO} Ref
{IO} a
419. builtin.Scope.ref : a
420. builtin.Scope.ref : a
->{Scope
s} Ref
{Scope
s}
a
420. builtin.Text.repeat : Nat
421. builtin.Text.repeat : Nat
-> Text
-> Text
421. builtin.Pattern.replicate : Nat
422. builtin.Pattern.replicate : Nat
-> Nat
-> Pattern
a
-> Pattern
a
422. builtin.io2.STM.retry : '{STM} a
423. builtin.Text.reverse : Text
423. builtin.io2.STM.retry : '{STM} a
424. builtin.Text.reverse : Text
-> Text
424. builtin.Float.round : Float
425. builtin.Float.round : Float
-> Int
425. builtin.Pattern.run : Pattern
426. builtin.Pattern.run : Pattern
a
-> a
-> Optional
( [a],
a)
426. builtin.Scope.run : (∀ s.
427. builtin.Scope.run : (∀ s.
'{g,
Scope s} r)
->{g} r
427. builtin.io2.Clock.internals.sec : TimeSpec
428. builtin.io2.Clock.internals.sec : TimeSpec
-> Int
428. builtin.Char.Class.separator : Class
429. builtin.Code.serialize : Code
429. builtin.Char.Class.separator : Class
430. builtin.Code.serialize : Code
-> Bytes
430. builtin.Value.serialize : Value
431. builtin.Value.serialize : Value
-> Bytes
431. builtin.io2.Tls.ClientConfig.certificates.set : [SignedCert]
432. builtin.io2.Tls.ClientConfig.certificates.set : [SignedCert]
-> ClientConfig
-> ClientConfig
432. builtin.io2.Tls.ServerConfig.certificates.set : [SignedCert]
433. builtin.io2.Tls.ServerConfig.certificates.set : [SignedCert]
-> ServerConfig
-> ServerConfig
433. builtin.io2.TLS.ClientConfig.ciphers.set : [Cipher]
434. builtin.io2.TLS.ClientConfig.ciphers.set : [Cipher]
-> ClientConfig
-> ClientConfig
434. builtin.io2.Tls.ServerConfig.ciphers.set : [Cipher]
435. builtin.io2.Tls.ServerConfig.ciphers.set : [Cipher]
-> ServerConfig
-> ServerConfig
435. builtin.io2.Tls.ClientConfig.versions.set : [Version]
436. builtin.io2.Tls.ClientConfig.versions.set : [Version]
-> ClientConfig
-> ClientConfig
436. builtin.io2.Tls.ServerConfig.versions.set : [Version]
437. builtin.io2.Tls.ServerConfig.versions.set : [Version]
-> ServerConfig
-> ServerConfig
437. builtin.Int.shiftLeft : Int
438. builtin.Int.shiftLeft : Int
-> Nat
-> Int
438. builtin.Nat.shiftLeft : Nat
439. builtin.Nat.shiftLeft : Nat
-> Nat
-> Nat
439. builtin.Int.shiftRight : Int
440. builtin.Int.shiftRight : Int
-> Nat
-> Int
440. builtin.Nat.shiftRight : Nat
441. builtin.Nat.shiftRight : Nat
-> Nat
-> Nat
441. builtin.Int.signum : Int
442. builtin.Int.signum : Int
-> Int
442. builtin.Float.sin : Float
443. builtin.Float.sin : Float
-> Float
443. builtin.Float.sinh : Float
444. builtin.Float.sinh : Float
-> Float
444. builtin.Bytes.size : Bytes
445. builtin.Bytes.size : Bytes
-> Nat
445. builtin.ImmutableArray.size : ImmutableArray
446. builtin.ImmutableArray.size : ImmutableArray
a
-> Nat
446. builtin.ImmutableByteArray.size : ImmutableByteArray
447. builtin.ImmutableByteArray.size : ImmutableByteArray
-> Nat
447. builtin.List.size : [a]
448. builtin.List.size : [a]
-> Nat
448. builtin.MutableArray.size : MutableArray
449. builtin.MutableArray.size : MutableArray
g a
-> Nat
449. builtin.MutableByteArray.size : MutableByteArray
450. builtin.MutableByteArray.size : MutableByteArray
g
-> Nat
450. builtin.Text.size : Text
451. builtin.Text.size : Text
-> Nat
451. builtin.Text.patterns.space : Pattern
452. builtin.Text.patterns.space : Pattern
Text
452. builtin.Float.sqrt : Float
453. builtin.Float.sqrt : Float
-> Float
453. builtin.io2.IO.process.start : Text
454. builtin.io2.IO.process.start : Text
-> [Text]
->{IO} ( Handle,
Handle,
Handle,
ProcessHandle)
454. builtin.io2.IO.stdHandle : StdHandle
455. builtin.io2.IO.stdHandle : StdHandle
-> Handle
455. builtin.Nat.sub : Nat
456. builtin.Nat.sub : Nat
-> Nat
-> Int
456. builtin.io2.TVar.swap : TVar a
457. builtin.io2.TVar.swap : TVar a
-> a
->{STM} a
457. builtin.Char.Class.symbol : Class
458. builtin.io2.IO.systemTimeMicroseconds : '{IO} Int
459. builtin.io2.Clock.internals.systemTimeZone : Int
458. builtin.Char.Class.symbol : Class
459. builtin.io2.IO.systemTimeMicroseconds : '{IO} Int
460. builtin.io2.Clock.internals.systemTimeZone : Int
->{IO} ( Int,
Nat,
Text)
460. builtin.Bytes.take : Nat
461. builtin.Bytes.take : Nat
-> Bytes
-> Bytes
461. builtin.List.take : Nat
462. builtin.List.take : Nat
-> [a]
-> [a]
462. builtin.Text.take : Nat
463. builtin.Text.take : Nat
-> Text
-> Text
463. builtin.Float.tan : Float
464. builtin.Float.tan : Float
-> Float
464. builtin.Float.tanh : Float
465. builtin.Float.tanh : Float
-> Float
465. builtin.io2.Clock.internals.threadCPUTime : '{IO} Either
466. builtin.io2.Clock.internals.threadCPUTime : '{IO} Either
Failure
TimeSpec
466. builtin.Bytes.toBase16 : Bytes
467. builtin.Bytes.toBase16 : Bytes
-> Bytes
467. builtin.Bytes.toBase32 : Bytes
468. builtin.Bytes.toBase32 : Bytes
-> Bytes
468. builtin.Bytes.toBase64 : Bytes
469. builtin.Bytes.toBase64 : Bytes
-> Bytes
469. builtin.Bytes.toBase64UrlUnpadded : Bytes
470. builtin.Bytes.toBase64UrlUnpadded : Bytes
-> Bytes
470. builtin.Text.toCharList : Text
471. builtin.Text.toCharList : Text
-> [Char]
471. builtin.Int.toFloat : Int
472. builtin.Int.toFloat : Int
-> Float
472. builtin.Nat.toFloat : Nat
473. builtin.Nat.toFloat : Nat
-> Float
473. builtin.Nat.toInt : Nat
474. builtin.Nat.toInt : Nat
-> Int
474. builtin.Bytes.toList : Bytes
475. builtin.Bytes.toList : Bytes
-> [Nat]
475. builtin.Text.toLowercase : Text
476. builtin.Text.toLowercase : Text
-> Text
476. builtin.Char.toNat : Char
477. builtin.Char.toNat : Char
-> Nat
477. builtin.Float.toRepresentation : Float
478. builtin.Float.toRepresentation : Float
-> Nat
478. builtin.Int.toRepresentation : Int
479. builtin.Int.toRepresentation : Int
-> Nat
479. builtin.Char.toText : Char
480. builtin.Char.toText : Char
-> Text
480. builtin.Debug.toText : a
481. builtin.Debug.toText : a
-> Optional
(Either
Text
Text)
481. builtin.Float.toText : Float
482. builtin.Float.toText : Float
-> Text
482. builtin.Handle.toText : Handle
483. builtin.Handle.toText : Handle
-> Text
483. builtin.Int.toText : Int
484. builtin.Int.toText : Int
-> Text
484. builtin.Nat.toText : Nat
485. builtin.Nat.toText : Nat
-> Text
485. builtin.Socket.toText : Socket
486. builtin.Socket.toText : Socket
-> Text
486. builtin.Link.Term.toText : Term
487. builtin.Link.Term.toText : Term
-> Text
487. builtin.ThreadId.toText : ThreadId
488. builtin.ThreadId.toText : ThreadId
-> Text
488. builtin.Text.toUppercase : Text
489. builtin.Text.toUppercase : Text
-> Text
489. builtin.Text.toUtf8 : Text
490. builtin.Text.toUtf8 : Text
-> Bytes
490. builtin.todo : a -> b
491. builtin.Debug.trace : Text
491. builtin.todo : a -> b
492. builtin.Debug.trace : Text
-> a
-> ()
492. builtin.Int.trailingZeros : Int
493. builtin.Int.trailingZeros : Int
-> Nat
493. builtin.Nat.trailingZeros : Nat
494. builtin.Nat.trailingZeros : Nat
-> Nat
494. builtin.Float.truncate : Float
495. builtin.Float.truncate : Float
-> Int
495. builtin.Int.truncate0 : Int
496. builtin.Int.truncate0 : Int
-> Nat
496. builtin.io2.IO.tryEval : '{IO} a
497. builtin.io2.IO.tryEval : '{IO} a
->{IO,
Exception} a
497. builtin.io2.Promise.tryRead : Promise
498. builtin.io2.Promise.tryRead : Promise
a
->{IO} Optional
a
498. builtin.io2.MVar.tryTake : MVar a
499. builtin.io2.MVar.tryTake : MVar a
->{IO} Optional
a
499. builtin.Text.uncons : Text
500. builtin.Text.uncons : Text
-> Optional
( Char,
Text)
500. builtin.Any.unsafeExtract : Any
501. builtin.Any.unsafeExtract : Any
-> a
501. builtin.Text.unsnoc : Text
502. builtin.Text.unsnoc : Text
-> Optional
( Text,
Char)
502. builtin.Char.Class.upper : Class
503. builtin.Code.validate : [( Term,
503. builtin.Char.Class.upper : Class
504. builtin.Code.validate : [( Term,
Code)]
->{IO} Optional
Failure
504. builtin.io2.validateSandboxed : [Term]
505. builtin.io2.validateSandboxed : [Term]
-> a
-> Boolean
505. builtin.Value.value : a
506. builtin.Value.value : a
-> Value
506. builtin.io2.IO.process.wait : ProcessHandle
507. builtin.io2.IO.process.wait : ProcessHandle
->{IO} Nat
507. builtin.Debug.watch : Text
508. builtin.Debug.watch : Text
-> a
-> a
508. builtin.Char.Class.whitespace : Class
509. builtin.MutableArray.write : MutableArray
509. builtin.Char.Class.whitespace : Class
510. builtin.MutableArray.write : MutableArray
g a
-> Nat
-> a
->{g,
Exception} ()
510. builtin.io2.Promise.write : Promise
511. builtin.io2.Promise.write : Promise
a
-> a
->{IO} Boolean
511. builtin.Ref.write : Ref g a
512. builtin.Ref.write : Ref g a
-> a
->{g} ()
512. builtin.io2.TVar.write : TVar a
513. builtin.io2.TVar.write : TVar a
-> a
->{STM} ()
513. builtin.MutableByteArray.write16be : MutableByteArray
514. builtin.MutableByteArray.write16be : MutableByteArray
g
-> Nat
-> Nat
->{g,
Exception} ()
514. builtin.MutableByteArray.write32be : MutableByteArray
515. builtin.MutableByteArray.write32be : MutableByteArray
g
-> Nat
-> Nat
->{g,
Exception} ()
515. builtin.MutableByteArray.write64be : MutableByteArray
516. builtin.MutableByteArray.write64be : MutableByteArray
g
-> Nat
-> Nat
->{g,
Exception} ()
516. builtin.MutableByteArray.write8 : MutableByteArray
517. builtin.MutableByteArray.write8 : MutableByteArray
g
-> Nat
-> Nat
->{g,
Exception} ()
517. builtin.Int.xor : Int
518. builtin.Int.xor : Int
-> Int
-> Int
518. builtin.Nat.xor : Nat
519. builtin.Nat.xor : Nat
-> Nat
-> Nat

View File

@ -10,7 +10,7 @@ zonk = 0
```ucm
.foo> add
.> project.create foo
.> project.create-empty foo
.> merge foo foo/main
```
@ -23,7 +23,7 @@ foo/main> add
```
```ucm
.> project.create bar
.> project.create-empty bar
bar/main> merge foo/main
bar/main> branch /topic
```

View File

@ -30,10 +30,22 @@ zonk = 0
zonk : Nat
.> project.create foo
.> project.create-empty foo
🎉 I've created the project foo.
🎨 Type `ui` to explore this project's code in your browser.
🔭 Discover libraries at https://share.unison-lang.org
📖 Use `help-topic projects` to learn more about projects.
Write your first Unison code with UCM:
1. Open scratch.u.
2. Write some Unison code and save the file.
3. In UCM, type `add` to save it to your new project.
🎉 🥳 Happy coding!
.> merge foo foo/main
Here's what's changed in foo/main after the merge:
@ -74,10 +86,22 @@ foo/main> add
```
```ucm
.> project.create bar
.> project.create-empty bar
🎉 I've created the project bar.
🎨 Type `ui` to explore this project's code in your browser.
🔭 Discover libraries at https://share.unison-lang.org
📖 Use `help-topic projects` to learn more about projects.
Write your first Unison code with UCM:
1. Open scratch.u.
2. Write some Unison code and save the file.
3. In UCM, type `add` to save it to your new project.
🎉 🥳 Happy coding!
bar/main> merge foo/main
Here's what's changed in the current namespace after the

View File

@ -59,17 +59,17 @@ y = 2
most recent, along with the command that got us there. Try:
`fork 2 .old`
`fork #sfboh4r465 .old` to make an old namespace
`fork #3amtojjv8u .old` to make an old namespace
accessible again,
`reset-root #sfboh4r465` to reset the root namespace and
`reset-root #3amtojjv8u` to reset the root namespace and
its history to that of the
specified namespace.
When Root Hash Action
1. now #6oglurikek add
2. now #sfboh4r465 add
3. now #t4v947uir6 builtins.merge
1. now #8hu49338cf add
2. now #3amtojjv8u add
3. now #fhvlkucguq builtins.merge
4. #sg60bvjo91 history starts here
Tip: Use `diff.namespace 1 7` to compare namespaces between

View File

@ -11,7 +11,7 @@ someterm = 18
```
```ucm
.> project.create foo
.> project.create-empty foo
foo/main> add
```

View File

@ -18,10 +18,22 @@ someterm = 18
```
```ucm
.> project.create foo
.> project.create-empty foo
🎉 I've created the project foo.
🎨 Type `ui` to explore this project's code in your browser.
🔭 Discover libraries at https://share.unison-lang.org
📖 Use `help-topic projects` to learn more about projects.
Write your first Unison code with UCM:
1. Open scratch.u.
2. Write some Unison code and save the file.
3. In UCM, type `add` to save it to your new project.
🎉 🥳 Happy coding!
foo/main> add
⍟ I've added these definitions:

View File

@ -29,7 +29,7 @@ foo.a = 5
# reset branch
```ucm
.> project.create foo
.> project.create-empty foo
foo/main> history
```

View File

@ -26,13 +26,13 @@ a = 5
Note: The most recent namespace hash is immediately below this
message.
⊙ 1. #d31cd19hmj
⊙ 1. #9o9nce99ll
+ Adds / updates:
a
□ 2. #t4v947uir6 (start of history)
□ 2. #fhvlkucguq (start of history)
.> reset 2
@ -45,7 +45,7 @@ a = 5
□ 1. #t4v947uir6 (start of history)
□ 1. #fhvlkucguq (start of history)
```
```unison
@ -79,13 +79,13 @@ foo.a = 5
Note: The most recent namespace hash is immediately below this
message.
⊙ 1. #fnbh3f6bi4
⊙ 1. #6q9qb5j7he
+ Adds / updates:
foo.a
□ 2. #t4v947uir6 (start of history)
□ 2. #fhvlkucguq (start of history)
.> reset 1 foo
@ -99,10 +99,22 @@ foo.a = 5
# reset branch
```ucm
.> project.create foo
.> project.create-empty foo
🎉 I've created the project foo.
🎨 Type `ui` to explore this project's code in your browser.
🔭 Discover libraries at https://share.unison-lang.org
📖 Use `help-topic projects` to learn more about projects.
Write your first Unison code with UCM:
1. Open scratch.u.
2. Write some Unison code and save the file.
3. In UCM, type `add` to save it to your new project.
🎉 🥳 Happy coding!
foo/main> history
☝️ The namespace is empty.

View File

@ -13,7 +13,7 @@ Let's look at some examples. We'll start with a namespace with just the builtins
□ 1. #lcn24e0a6c (start of history)
□ 1. #vpkls27v13 (start of history)
.> fork builtin builtin2
@ -42,21 +42,21 @@ Now suppose we `fork` a copy of builtin, then rename `Nat.+` to `frobnicate`, th
Note: The most recent namespace hash is immediately below this
message.
⊙ 1. #ps6sgdb6eb
⊙ 1. #gtktavsknb
> Moves:
Original name New name
Nat.frobnicate Nat.+
⊙ 2. #pmj762mtub
⊙ 2. #tfkgj5mep6
> Moves:
Original name New name
Nat.+ Nat.frobnicate
□ 3. #lcn24e0a6c (start of history)
□ 3. #vpkls27v13 (start of history)
```
If we merge that back into `builtin`, we get that same chain of history:
@ -73,21 +73,21 @@ If we merge that back into `builtin`, we get that same chain of history:
Note: The most recent namespace hash is immediately below this
message.
⊙ 1. #ps6sgdb6eb
⊙ 1. #gtktavsknb
> Moves:
Original name New name
Nat.frobnicate Nat.+
⊙ 2. #pmj762mtub
⊙ 2. #tfkgj5mep6
> Moves:
Original name New name
Nat.+ Nat.frobnicate
□ 3. #lcn24e0a6c (start of history)
□ 3. #vpkls27v13 (start of history)
```
Let's try again, but using a `merge.squash` (or just `squash`) instead. The history will be unchanged:
@ -108,7 +108,7 @@ Let's try again, but using a `merge.squash` (or just `squash`) instead. The hist
□ 1. #lcn24e0a6c (start of history)
□ 1. #vpkls27v13 (start of history)
```
The churn that happened in `mybuiltin` namespace ended up back in the same spot, so the squash merge of that namespace with our original namespace had no effect.
@ -499,13 +499,13 @@ This checks to see that squashing correctly preserves deletions:
Note: The most recent namespace hash is immediately below this
message.
⊙ 1. #9hnba2qr13
⊙ 1. #nd5hkv52vo
- Deletes:
Nat.* Nat.+
□ 2. #lcn24e0a6c (start of history)
□ 2. #vpkls27v13 (start of history)
```
Notice that `Nat.+` and `Nat.*` are deleted by the squash, and we see them deleted in one atomic step in the history.

View File

@ -11,8 +11,8 @@ someterm = 18
```
```ucm
.> project.create foo
.> project.create bar
.> project.create-empty foo
.> project.create-empty bar
foo/main> add
foo/main> branch bar
foo/main> branch topic

View File

@ -18,14 +18,38 @@ someterm = 18
```
```ucm
.> project.create foo
.> project.create-empty foo
🎉 I've created the project foo.
.> project.create bar
🎨 Type `ui` to explore this project's code in your browser.
🔭 Discover libraries at https://share.unison-lang.org
📖 Use `help-topic projects` to learn more about projects.
Write your first Unison code with UCM:
1. Open scratch.u.
2. Write some Unison code and save the file.
3. In UCM, type `add` to save it to your new project.
🎉 🥳 Happy coding!
.> project.create-empty bar
🎉 I've created the project bar.
🎨 Type `ui` to explore this project's code in your browser.
🔭 Discover libraries at https://share.unison-lang.org
📖 Use `help-topic projects` to learn more about projects.
Write your first Unison code with UCM:
1. Open scratch.u.
2. Write some Unison code and save the file.
3. In UCM, type `add` to save it to your new project.
🎉 🥳 Happy coding!
foo/main> add
⍟ I've added these definitions: