mirror of
https://github.com/roc-lang/roc.git
synced 2024-10-05 06:37:26 +03:00
Merge remote-tracking branch 'origin/main' into suffix-tutorial
This commit is contained in:
commit
73d741de5a
@ -11,7 +11,7 @@ env:
|
||||
# use .tar.gz for quick testing
|
||||
ARCHIVE_FORMAT: .tar.br
|
||||
# Make a new basic-cli git tag and set it here before starting this workflow
|
||||
RELEASE_TAG: 0.9.0
|
||||
RELEASE_TAG: 0.9.1
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
@ -158,7 +158,11 @@ jobs:
|
||||
- name: rename nightly folder
|
||||
run: mv roc_nightly* roc_nightly
|
||||
|
||||
- run: git clone https://github.com/roc-lang/basic-cli.git
|
||||
- run: |
|
||||
git clone https://github.com/roc-lang/basic-cli.git
|
||||
cd basic-cli
|
||||
git checkout $RELEASE_TAG
|
||||
cd ..
|
||||
|
||||
- run: cp macos-apple-silicon-files/* ./basic-cli/platform
|
||||
|
||||
|
5
.github/workflows/test_nightly_many_os.yml
vendored
5
.github/workflows/test_nightly_many_os.yml
vendored
@ -18,12 +18,15 @@ jobs:
|
||||
with:
|
||||
version: 0.11.0
|
||||
|
||||
- name: Install zlib on macOS-13
|
||||
if: matrix.os == 'macos-13'
|
||||
run: brew install zlib
|
||||
|
||||
- name: get the latest release archive for linux (x86_64)
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
run: |
|
||||
curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-latest.tar.gz
|
||||
|
||||
|
||||
- name: get the latest release archive for macos (x86_64)
|
||||
if: startsWith(matrix.os, 'macos')
|
||||
run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_x86_64-latest.tar.gz
|
||||
|
51
Cargo.lock
generated
51
Cargo.lock
generated
@ -2216,12 +2216,27 @@ dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"spin 0.5.2",
|
||||
"untrusted 0.7.1",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"libc",
|
||||
"spin 0.9.8",
|
||||
"untrusted 0.9.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rlimit"
|
||||
version = "0.9.1"
|
||||
@ -2650,7 +2665,7 @@ name = "roc_ident"
|
||||
version = "0.0.1"
|
||||
|
||||
[[package]]
|
||||
name = "roc_lang_srv"
|
||||
name = "roc_language_server"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
@ -3229,12 +3244,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.21.7"
|
||||
version = "0.21.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8"
|
||||
checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"ring 0.17.8",
|
||||
"rustls-webpki",
|
||||
"sct",
|
||||
]
|
||||
@ -3250,12 +3265,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.101.6"
|
||||
version = "0.101.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe"
|
||||
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
"ring 0.17.8",
|
||||
"untrusted 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3370,8 +3385,8 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
"ring 0.16.20",
|
||||
"untrusted 0.7.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3600,6 +3615,12 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
@ -4278,6 +4299,12 @@ version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.4.1"
|
||||
|
@ -27,7 +27,7 @@ members = [
|
||||
"crates/wasi-libc-sys",
|
||||
"crates/wasm_module",
|
||||
"crates/wasm_interp",
|
||||
"crates/lang_srv",
|
||||
"crates/language_server",
|
||||
]
|
||||
|
||||
exclude = [
|
||||
|
@ -33,6 +33,8 @@ If you would like your company to become a corporate sponsor of Roc's developmen
|
||||
|
||||
We'd also like to express our gratitude to our generous [individual sponsors](https://github.com/sponsors/roc-lang/)! A special thanks to those sponsoring $25/month or more:
|
||||
|
||||
* [Angelo Ceccato](https://github.com/AngeloChecked)
|
||||
* [Niclas Overby](https://github.com/noverby)
|
||||
* [Krzysztof G.](https://github.com/krzysztofgb)
|
||||
* [Sam Mohr](https://github.com/smores56)
|
||||
* [Steven Chen](https://github.com/megakilo)
|
||||
|
@ -948,7 +948,7 @@ mod cli_run {
|
||||
This roc file can print its own source code. The source is:
|
||||
|
||||
app "ingested-file"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [
|
||||
pf.Stdout,
|
||||
"ingested-file.roc" as ownCode : Str,
|
||||
|
@ -1001,7 +1001,7 @@ bitwiseNot = \n ->
|
||||
## ```roc
|
||||
## shiftLeftBy 0b0000_0011 2 == 0b0000_1100
|
||||
##
|
||||
## 0b0000_0101 |> shiftLeftBy 2 == 0b0000_1100
|
||||
## 0b0000_0101 |> shiftLeftBy 2 == 0b0001_0100
|
||||
## ```
|
||||
## In some languages `shiftLeftBy` is implemented as a binary operator `<<`.
|
||||
shiftLeftBy : Int a, U8 -> Int a
|
||||
|
@ -189,7 +189,9 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
}) => Body(
|
||||
}) => desugar_value_def_suffixed(
|
||||
arena,
|
||||
Body(
|
||||
loc_pattern,
|
||||
apply_task_await(
|
||||
arena,
|
||||
@ -199,6 +201,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
|
||||
wrap_in_task_ok(arena, sub_new),
|
||||
),
|
||||
),
|
||||
),
|
||||
Err(..) => Body(
|
||||
loc_pattern,
|
||||
arena.alloc(Loc::at(loc_expr.region, MalformedSuffixed(loc_expr))),
|
||||
@ -226,7 +229,9 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
}) => AnnotatedBody {
|
||||
}) => desugar_value_def_suffixed(
|
||||
arena,
|
||||
AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
comment,
|
||||
@ -239,6 +244,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
|
||||
wrap_in_task_ok(arena, sub_new),
|
||||
),
|
||||
},
|
||||
),
|
||||
Err(..) => AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
|
@ -6,7 +6,7 @@ use roc_error_macros::internal_error;
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_parse::ast::Expr::{self, *};
|
||||
use roc_parse::ast::{is_loc_expr_suffixed, wrap_in_task_ok, Pattern, ValueDef};
|
||||
use roc_parse::ast::{is_loc_expr_suffixed, wrap_in_task_ok, Pattern, ValueDef, WhenBranch};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use std::cell::Cell;
|
||||
|
||||
@ -564,11 +564,61 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
|
||||
}
|
||||
|
||||
pub fn unwrap_suffixed_expression_when_help<'a>(
|
||||
_arena: &'a Bump,
|
||||
arena: &'a Bump,
|
||||
loc_expr: &'a Loc<Expr<'a>>,
|
||||
_maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
|
||||
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
|
||||
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
|
||||
Ok(loc_expr)
|
||||
match loc_expr.value {
|
||||
Expr::When(condition, branches) => {
|
||||
|
||||
// first unwrap any when branches values
|
||||
// e.g.
|
||||
// when foo is
|
||||
// [] -> line! "bar"
|
||||
// _ -> line! "baz"
|
||||
for (branch_index, WhenBranch{value: branch_loc_expr,patterns, guard}) in branches.iter().enumerate() {
|
||||
|
||||
// if the branch isn't suffixed we can leave it alone
|
||||
if is_loc_expr_suffixed(branch_loc_expr) {
|
||||
let unwrapped_branch_value = match unwrap_suffixed_expression(arena, branch_loc_expr, None) {
|
||||
Ok(unwrapped_branch_value) => unwrapped_branch_value,
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => apply_task_await(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new),
|
||||
Err(..) => return Err(EUnwrapped::Malformed),
|
||||
};
|
||||
|
||||
let new_branch = WhenBranch{value: *unwrapped_branch_value, patterns, guard: *guard};
|
||||
let mut new_branches = Vec::new_in(arena);
|
||||
let (before, rest) = branches.split_at(branch_index);
|
||||
let after = &rest[1..];
|
||||
|
||||
new_branches.extend_from_slice(before);
|
||||
new_branches.push(arena.alloc(new_branch));
|
||||
new_branches.extend_from_slice(after);
|
||||
|
||||
let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(condition, arena.alloc_slice_copy(new_branches.as_slice()))));
|
||||
|
||||
return unwrap_suffixed_expression(arena, new_when, maybe_def_pat);
|
||||
}
|
||||
}
|
||||
|
||||
// then unwrap the when condition
|
||||
match unwrap_suffixed_expression(arena, condition, None) {
|
||||
Ok(unwrapped_condition) => {
|
||||
let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(unwrapped_condition, branches)));
|
||||
Ok(new_when)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(sub_new, branches)));
|
||||
let applied_task_await = apply_task_await(arena,loc_expr.region,sub_arg,sub_pat,new_when);
|
||||
Ok(applied_task_await)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..))
|
||||
| Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed)
|
||||
}
|
||||
|
||||
}
|
||||
_ => internal_error!("unreachable, expected a When node to be passed into unwrap_suffixed_expression_defs_help"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_suffixed_expression_defs_help<'a>(
|
||||
@ -807,15 +857,21 @@ fn is_matching_empty_record<'a>(
|
||||
is_empty_record && is_pattern_empty_record
|
||||
}
|
||||
|
||||
fn is_matching_intermediate_answer<'a>(
|
||||
pub fn is_matching_intermediate_answer<'a>(
|
||||
loc_pat: &'a Loc<Pattern<'a>>,
|
||||
loc_expr: &'a Loc<Expr<'a>>,
|
||||
loc_new: &'a Loc<Expr<'a>>,
|
||||
) -> bool {
|
||||
let pat_ident = match loc_pat.value {
|
||||
Pattern::Identifier { ident, .. } => Some(ident),
|
||||
_ => None,
|
||||
};
|
||||
let exp_ident = match extract_wrapped_task_ok_value(loc_expr) {
|
||||
let exp_ident = match loc_new.value {
|
||||
Expr::Var {
|
||||
module_name, ident, ..
|
||||
} if module_name.is_empty() && ident.starts_with('#') => Some(ident),
|
||||
_ => None,
|
||||
};
|
||||
let exp_ident_in_task = match extract_wrapped_task_ok_value(loc_new) {
|
||||
Some(task_expr) => match task_expr.value {
|
||||
Expr::Var {
|
||||
module_name, ident, ..
|
||||
@ -824,8 +880,9 @@ fn is_matching_intermediate_answer<'a>(
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
match (pat_ident, exp_ident) {
|
||||
(Some(a), Some(b)) => a == b,
|
||||
match (pat_ident, exp_ident, exp_ident_in_task) {
|
||||
(Some(a), Some(b), None) => a == b,
|
||||
(Some(a), None, Some(b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -420,6 +420,26 @@ mod suffixed_tests {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nested suffixed expressions
|
||||
```roc
|
||||
run = line! (nextMsg!)
|
||||
|
||||
run = Task.await nextMsg \#!a0 -> line! (#!a0)
|
||||
|
||||
run = Task.await nextMsg \#!a0 -> line (#!a0)
|
||||
```
|
||||
*/
|
||||
#[test]
|
||||
fn nested_simple() {
|
||||
run_test(
|
||||
r#"
|
||||
run = line! (nextMsg!)
|
||||
"#,
|
||||
r##"Defs { tags: [Index(2147483648)], regions: [@0-22], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-3 Identifier { ident: "run", suffixed: 0 }, @0-22 Apply(@0-22 Var { module_name: "Task", ident: "await", suffixed: 0 }, [Var { module_name: "", ident: "nextMsg", suffixed: 0 }, @0-22 Closure([Identifier { ident: "#!a0", suffixed: 0 }], @0-22 Apply(@0-22 Var { module_name: "", ident: "line", suffixed: 0 }, [@13-21 ParensAround(Var { module_name: "", ident: "#!a0", suffixed: 0 })], Space))], BangSuffix))] }"##,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nested suffixed expressions
|
||||
```roc
|
||||
@ -437,9 +457,8 @@ mod suffixed_tests {
|
||||
Task.await [foo (#!a0) (blah stuff)] \z -> doSomething z
|
||||
```
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn nested_suffixed() {
|
||||
fn nested_complex() {
|
||||
run_test(
|
||||
r#"
|
||||
main =
|
||||
@ -665,4 +684,143 @@ mod suffixed_tests {
|
||||
r#"Defs { tags: [Index(2147483648)], regions: [@0-103], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "copy", suffixed: 0 }, @7-103 Closure([@8-9 Identifier { ident: "a", suffixed: 0 }, @10-11 Identifier { ident: "b", suffixed: 0 }], @36-42 Apply(@36-42 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@36-42 Apply(@36-42 Var { module_name: "", ident: "line", suffixed: 0 }, [@37-42 Str(PlainLine("FOO"))], Space), @36-42 Closure([@36-42 RecordDestructure([])], @60-103 Apply(@60-103 Var { module_name: "", ident: "mapErr", suffixed: 0 }, [@60-72 Apply(@60-67 Var { module_name: "CMD", ident: "new", suffixed: 0 }, [@68-72 Str(PlainLine("cp"))], Space), @100-103 Tag("ERR")], BinOp(Pizza)))], BangSuffix)))] }"#,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwrap a when expression
|
||||
```roc
|
||||
list =
|
||||
when getList! is
|
||||
[] -> "empty"
|
||||
_ -> "non-empty"
|
||||
|
||||
list =
|
||||
Task.await getList \#!a0 ->
|
||||
when #!a0 is
|
||||
[] -> "empty"
|
||||
_ -> "non-empty"
|
||||
```
|
||||
*/
|
||||
#[test]
|
||||
fn when_simple() {
|
||||
run_test(
|
||||
r#"
|
||||
list =
|
||||
when getList! is
|
||||
[] -> "empty"
|
||||
_ -> "non-empty"
|
||||
"#,
|
||||
r##"Defs { tags: [Index(2147483648)], regions: [@0-111], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "list", suffixed: 0 }, @24-111 Apply(@24-111 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@29-37 Var { module_name: "", ident: "getList", suffixed: 0 }, @24-111 Closure([@29-37 Identifier { ident: "#!a0", suffixed: 0 }], @24-111 When(@29-37 Var { module_name: "", ident: "#!a0", suffixed: 0 }, [WhenBranch { patterns: [@61-63 List([])], value: @67-74 Str(PlainLine("empty")), guard: None }, WhenBranch { patterns: [@95-96 Underscore("")], value: @100-111 Str(PlainLine("non-empty")), guard: None }]))], BangSuffix))] }"##,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwrap a when expression
|
||||
```roc
|
||||
list =
|
||||
when getList! is
|
||||
[] ->
|
||||
line! "foo"
|
||||
line! "bar"
|
||||
_ ->
|
||||
ok {}
|
||||
|
||||
list =
|
||||
Task.await getList \#!a0 ->
|
||||
when getList is
|
||||
[] ->
|
||||
line! "foo"
|
||||
line! "bar"
|
||||
_ ->
|
||||
ok {}
|
||||
|
||||
list =
|
||||
Task.await getList \#!a0 ->
|
||||
when getList is
|
||||
[] ->
|
||||
Task.await line "foo" \{} -> line! "bar"
|
||||
_ ->
|
||||
ok {}
|
||||
```
|
||||
*/
|
||||
#[test]
|
||||
fn when_branches() {
|
||||
run_test(
|
||||
r#"
|
||||
list =
|
||||
when getList! is
|
||||
[] ->
|
||||
line! "foo"
|
||||
line! "bar"
|
||||
_ ->
|
||||
ok {}
|
||||
"#,
|
||||
r##"Defs { tags: [Index(2147483648)], regions: [@0-195], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "list", suffixed: 0 }, @24-195 Apply(@24-195 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@29-37 Var { module_name: "", ident: "getList", suffixed: 0 }, @24-195 Closure([@29-37 Identifier { ident: "#!a0", suffixed: 0 }], @24-195 When(@29-37 Var { module_name: "", ident: "#!a0", suffixed: 0 }, [WhenBranch { patterns: [@61-63 List([])], value: @97-103 Apply(@97-103 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@97-103 Apply(@97-103 Var { module_name: "", ident: "line", suffixed: 0 }, [@98-103 Str(PlainLine("foo"))], Space), @97-103 Closure([@97-103 RecordDestructure([])], @128-139 Apply(@128-139 Var { module_name: "", ident: "line", suffixed: 0 }, [@134-139 Str(PlainLine("bar"))], Space))], BangSuffix), guard: None }, WhenBranch { patterns: [@160-161 Underscore("")], value: @190-195 Apply(@190-192 Var { module_name: "", ident: "ok", suffixed: 0 }, [@193-195 Record([])], Space), guard: None }]))], BangSuffix))] }"##,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_suffix_inside_when() {
|
||||
run_test(
|
||||
r#"
|
||||
main =
|
||||
result = Stdin.line!
|
||||
|
||||
when result is
|
||||
End ->
|
||||
Task.ok {}
|
||||
|
||||
Input name ->
|
||||
Stdout.line! "Hello, $(name)"
|
||||
"#,
|
||||
r#"Defs { tags: [Index(2147483648)], regions: [@0-226], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @32-43 Apply(@32-43 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@32-43 Var { module_name: "Stdin", ident: "line", suffixed: 0 }, @32-43 Closure([@23-29 Identifier { ident: "result", suffixed: 0 }], @61-226 When(@66-72 Var { module_name: "", ident: "result", suffixed: 0 }, [WhenBranch { patterns: [@96-99 Tag("End")], value: @127-137 Apply(@127-134 Var { module_name: "Task", ident: "ok", suffixed: 0 }, [@135-137 Record([])], Space), guard: None }, WhenBranch { patterns: [@159-169 Apply(@159-164 Tag("Input"), [@165-169 Identifier { ident: "name", suffixed: 0 }])], value: @197-226 Apply(@197-226 Var { module_name: "Stdout", ident: "line", suffixed: 0 }, [@210-226 Str(Line([Plaintext("Hello, "), Interpolated(@220-224 Var { module_name: "", ident: "name", suffixed: 0 })]))], Space), guard: None }]))], BangSuffix))] }"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_suffixed_helpers {
|
||||
|
||||
use roc_can::suffixed::is_matching_intermediate_answer;
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_parse::ast::Expr;
|
||||
use roc_parse::ast::Pattern;
|
||||
use roc_region::all::Loc;
|
||||
|
||||
#[test]
|
||||
fn test_matching_answer() {
|
||||
let loc_pat = Loc::at_zero(Pattern::Identifier {
|
||||
ident: "#!a0",
|
||||
suffixed: 0,
|
||||
});
|
||||
let loc_new = Loc::at_zero(Expr::Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
suffixed: 0,
|
||||
});
|
||||
|
||||
std::assert!(is_matching_intermediate_answer(&loc_pat, &loc_new));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matching_answer_task_ok() {
|
||||
let loc_pat = Loc::at_zero(Pattern::Identifier {
|
||||
ident: "#!a0",
|
||||
suffixed: 0,
|
||||
});
|
||||
let intermetiate = &[&Loc::at_zero(Expr::Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
suffixed: 0,
|
||||
})];
|
||||
let task_ok = Loc::at_zero(Expr::Var {
|
||||
module_name: ModuleName::TASK,
|
||||
ident: "ok",
|
||||
suffixed: 0,
|
||||
});
|
||||
|
||||
let loc_new = Loc::at_zero(Expr::Apply(&task_ok, intermetiate, CalledVia::BangSuffix));
|
||||
|
||||
std::assert!(is_matching_intermediate_answer(&loc_pat, &loc_new));
|
||||
}
|
||||
}
|
||||
|
@ -2161,13 +2161,27 @@ fn report_unused_imported_modules(
|
||||
Occupied(entry) => entry.into_mut(),
|
||||
};
|
||||
|
||||
// TODO this outer conditional can be replaced by just the for loop
|
||||
// once we have Task as builtin. (Also the for loop doesn't need the "Task" check.)
|
||||
if !unused_imported_modules.is_empty() {
|
||||
let module_ids = Arc::clone(&state.arc_modules);
|
||||
let module_ids = module_ids.lock();
|
||||
|
||||
for (unused, region) in unused_imported_modules.drain() {
|
||||
if !unused.is_builtin() {
|
||||
let is_task_module = match module_ids.get_name(unused) {
|
||||
Some(name) => name.as_inner().as_str() == "Task",
|
||||
None => false,
|
||||
};
|
||||
|
||||
if !is_task_module {
|
||||
existing.push(roc_problem::can::Problem::UnusedModuleImport(
|
||||
unused, region,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unused, region) in unused_imports.drain() {
|
||||
existing.push(roc_problem::can::Problem::UnusedImport(unused, region));
|
||||
|
@ -1280,7 +1280,7 @@ fn roc_file_no_extension() {
|
||||
indoc!(
|
||||
r#"
|
||||
app "helloWorld"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [pf.Stdout]
|
||||
provides [main] to pf
|
||||
|
||||
|
@ -197,17 +197,12 @@ where
|
||||
)
|
||||
}
|
||||
|
||||
pub fn check_indent<'a, E>(
|
||||
indent_problem: fn(Position) -> E,
|
||||
inside_suffixed_statement: bool,
|
||||
) -> impl Parser<'a, (), E>
|
||||
pub fn check_indent<'a, E>(indent_problem: fn(Position) -> E) -> impl Parser<'a, (), E>
|
||||
where
|
||||
E: 'a,
|
||||
{
|
||||
let extra_spaces = if inside_suffixed_statement { 1 } else { 0 };
|
||||
|
||||
move |_, state: State<'a>, min_indent: u32| {
|
||||
if state.column() >= (min_indent + extra_spaces) {
|
||||
if state.column() >= min_indent {
|
||||
Ok((NoProgress, (), state))
|
||||
} else {
|
||||
Err((NoProgress, indent_problem(state.pos())))
|
||||
|
@ -1638,11 +1638,13 @@ fn finish_parsing_ability_def_help<'a>(
|
||||
Ok((MadeProgress, (type_def, def_region), state))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn parse_expr_operator<'a>(
|
||||
min_indent: u32,
|
||||
options: ExprParseOptions,
|
||||
mut expr_state: ExprState<'a>,
|
||||
loc_op: Loc<BinOp>,
|
||||
line_indent: u32,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
initial_state: State<'a>,
|
||||
@ -1687,7 +1689,8 @@ fn parse_expr_operator<'a>(
|
||||
}
|
||||
BinOp::Assignment => {
|
||||
let expr_region = expr_state.expr.region;
|
||||
let indented_more = min_indent + 1;
|
||||
|
||||
let indented_more = line_indent + 1;
|
||||
|
||||
let call = expr_state
|
||||
.validate_assignment_or_backpassing(arena, loc_op, EExpr::ElmStyleFunction)
|
||||
@ -1894,12 +1897,18 @@ fn parse_expr_end<'a>(
|
||||
state: State<'a>,
|
||||
initial_state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
let inner_min_indent = if options.suffixed_found {
|
||||
min_indent + 1
|
||||
} else {
|
||||
min_indent
|
||||
};
|
||||
|
||||
let parser = skip_first!(
|
||||
crate::blankspace::check_indent(EExpr::IndentEnd, options.suffixed_found),
|
||||
crate::blankspace::check_indent(EExpr::IndentEnd),
|
||||
loc_term_or_underscore(options)
|
||||
);
|
||||
|
||||
match parser.parse(arena, state.clone(), min_indent) {
|
||||
match parser.parse(arena, state.clone(), inner_min_indent) {
|
||||
Err((MadeProgress, f)) => Err((MadeProgress, f)),
|
||||
Ok((
|
||||
_,
|
||||
@ -2001,6 +2010,7 @@ fn parse_expr_end<'a>(
|
||||
Err((NoProgress, _)) => {
|
||||
let before_op = state.clone();
|
||||
// try an operator
|
||||
let line_indent = state.line_indent();
|
||||
match loc!(operator()).parse(arena, state.clone(), min_indent) {
|
||||
Err((MadeProgress, f)) => Err((MadeProgress, f)),
|
||||
Ok((_, loc_op, state)) => {
|
||||
@ -2011,6 +2021,7 @@ fn parse_expr_end<'a>(
|
||||
options,
|
||||
expr_state,
|
||||
loc_op,
|
||||
line_indent,
|
||||
arena,
|
||||
state,
|
||||
initial_state,
|
||||
|
@ -46,7 +46,7 @@ pub fn closure_param<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
|
||||
pub fn loc_pattern_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
move |arena, state: State<'a>, min_indent| {
|
||||
let (_, pattern, state) = loc_pattern_help_help().parse(arena, state, min_indent)?;
|
||||
let (_, pattern, state) = loc_pattern_help_help(true).parse(arena, state, min_indent)?;
|
||||
|
||||
let pattern_state = state.clone();
|
||||
|
||||
@ -82,11 +82,13 @@ pub fn loc_pattern_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>>
|
||||
}
|
||||
}
|
||||
|
||||
fn loc_pattern_help_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
fn loc_pattern_help_help<'a>(
|
||||
can_have_arguments: bool,
|
||||
) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
one_of!(
|
||||
specialize_err(EPattern::PInParens, loc_pattern_in_parens_help()),
|
||||
loc!(underscore_pattern_help()),
|
||||
loc_ident_pattern_help(true),
|
||||
loc_ident_pattern_help(can_have_arguments),
|
||||
loc!(specialize_err(
|
||||
EPattern::Record,
|
||||
crate::pattern::record_pattern_help()
|
||||
@ -143,7 +145,8 @@ fn loc_tag_pattern_arg<'a>(
|
||||
min_indent,
|
||||
)?;
|
||||
|
||||
let (_, loc_pat, state) = loc_parse_tag_pattern_arg().parse(arena, state, min_indent)?;
|
||||
// Cannot have arguments here, pass `false` to make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
|
||||
let (_, loc_pat, state) = loc_pattern_help_help(false).parse(arena, state, min_indent)?;
|
||||
|
||||
let Loc { region, value } = loc_pat;
|
||||
|
||||
@ -194,21 +197,6 @@ pub fn loc_implements_parser<'a>() -> impl Parser<'a, Loc<Implements<'a>>, EPatt
|
||||
)
|
||||
}
|
||||
|
||||
fn loc_parse_tag_pattern_arg<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
one_of!(
|
||||
specialize_err(EPattern::PInParens, loc_pattern_in_parens_help()),
|
||||
loc!(underscore_pattern_help()),
|
||||
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
|
||||
loc_ident_pattern_help(false),
|
||||
loc!(specialize_err(
|
||||
EPattern::Record,
|
||||
crate::pattern::record_pattern_help()
|
||||
)),
|
||||
loc!(string_like_pattern_help()),
|
||||
loc!(number_pattern_help())
|
||||
)
|
||||
}
|
||||
|
||||
fn loc_pattern_in_parens_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PInParens<'a>> {
|
||||
then(
|
||||
loc!(collection_trailing_sep_e!(
|
||||
|
@ -2,9 +2,9 @@
|
||||
name = "test_syntax-fuzz"
|
||||
publish = false
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
version.workspace = true
|
||||
version = "0.0.0"
|
||||
authors = ["Automatically generated"]
|
||||
edition = "2021"
|
||||
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
@ -12,8 +12,8 @@ cargo-fuzz = true
|
||||
[dependencies]
|
||||
test_syntax = { path = "../../test_syntax" }
|
||||
|
||||
bumpalo.workspace = true
|
||||
libfuzzer-sys.workspace = true
|
||||
bumpalo = { version = "3.12.0", features = ["collections"] }
|
||||
libfuzzer-sys = "0.4"
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
|
@ -1,7 +1,7 @@
|
||||
app "hello"
|
||||
packages {
|
||||
pf:
|
||||
"https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br",
|
||||
"https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br",
|
||||
}
|
||||
imports [pf.Stdout]
|
||||
provides [main] to pf
|
||||
|
@ -24,7 +24,7 @@ Full {
|
||||
Newline,
|
||||
],
|
||||
package_name: @31-145 PackageName(
|
||||
"https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br",
|
||||
"https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br",
|
||||
),
|
||||
},
|
||||
[
|
||||
|
@ -1,6 +1,6 @@
|
||||
app "hello"
|
||||
packages { pf:
|
||||
"https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br"
|
||||
"https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br"
|
||||
}
|
||||
imports [pf.Stdout]
|
||||
provides [main] to pf
|
||||
|
@ -0,0 +1,54 @@
|
||||
When(
|
||||
@5-10 Apply(
|
||||
@5-7 Tag(
|
||||
"Ok",
|
||||
),
|
||||
[
|
||||
@8-10 List(
|
||||
[],
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
[
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@18-23 SpaceBefore(
|
||||
Apply(
|
||||
@18-20 Tag(
|
||||
"Ok",
|
||||
),
|
||||
[
|
||||
@21-23 List(
|
||||
[],
|
||||
),
|
||||
],
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
],
|
||||
value: @27-29 Record(
|
||||
[],
|
||||
),
|
||||
guard: None,
|
||||
},
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@34-35 SpaceBefore(
|
||||
Underscore(
|
||||
"",
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
],
|
||||
value: @39-41 Record(
|
||||
[],
|
||||
),
|
||||
guard: None,
|
||||
},
|
||||
],
|
||||
)
|
@ -0,0 +1,3 @@
|
||||
when Ok [] is
|
||||
Ok [] -> {}
|
||||
_ -> {}
|
@ -482,6 +482,7 @@ mod test_snapshots {
|
||||
pass/when_in_function_python_style_indent.expr,
|
||||
pass/when_in_parens.expr,
|
||||
pass/when_in_parens_indented.expr,
|
||||
pass/when_result_list.expr,
|
||||
pass/when_with_alternative_patterns.expr,
|
||||
pass/when_with_function_application.expr,
|
||||
pass/when_with_negative_numbers.expr,
|
||||
|
@ -1,11 +1,13 @@
|
||||
interface TypeId
|
||||
exposes [TypeId, fromU64, toU64]
|
||||
exposes [TypeId, typeIDfromU64, typeIDtoU64]
|
||||
imports []
|
||||
|
||||
TypeId := U64 implements [Eq, Hash]
|
||||
TypeId := U64 implements [Eq, Hash, Inspect, Encoding]
|
||||
|
||||
toU64 : TypeId -> U64
|
||||
toU64 = \@TypeId x -> x
|
||||
# renamed here so we can import the functions directly as a workaround for
|
||||
# https://github.com/roc-lang/roc/issues/5477
|
||||
typeIDtoU64 : TypeId -> U64
|
||||
typeIDtoU64 = \@TypeId x -> x
|
||||
|
||||
fromU64 : U64 -> TypeId
|
||||
fromU64 = @TypeId
|
||||
typeIDfromU64 : U64 -> TypeId
|
||||
typeIDfromU64 = @TypeId
|
||||
|
@ -1,6 +1,6 @@
|
||||
interface Types
|
||||
exposes [Types, shape, size, alignment, target, walkShapes, entryPoints]
|
||||
imports [Shape.{ Shape }, TypeId.{ TypeId }, Target.{ Target }, TypeId]
|
||||
imports [Shape.{ Shape }, TypeId.{ TypeId, typeIDfromU64, typeIDtoU64 }, Target.{ Target }]
|
||||
|
||||
# TODO: switch AssocList uses to Dict once roc_std is updated.
|
||||
Tuple1 : [T Str TypeId]
|
||||
@ -23,7 +23,7 @@ Types := {
|
||||
## Names and types of the entry points of the program (e.g. mainForHost)
|
||||
entrypoints : List Tuple1,
|
||||
target : Target,
|
||||
}
|
||||
} implements [Inspect, Encoding]
|
||||
|
||||
target : Types -> Target
|
||||
target = \@Types types -> types.target
|
||||
@ -34,33 +34,33 @@ entryPoints = \@Types { entrypoints } -> entrypoints
|
||||
walkShapes : Types, state, (state, Shape, TypeId -> state) -> state
|
||||
walkShapes = \@Types { types: shapes }, originalState, update ->
|
||||
List.walkWithIndex shapes originalState \state, elem, index ->
|
||||
id = TypeId.fromU64 index
|
||||
id = typeIDfromU64 index
|
||||
|
||||
update state elem id
|
||||
|
||||
shape : Types, TypeId -> Shape
|
||||
shape = \@Types types, id ->
|
||||
when List.get types.types (TypeId.toU64 id) is
|
||||
when List.get types.types (typeIDtoU64 id) is
|
||||
Ok answer -> answer
|
||||
Err OutOfBounds ->
|
||||
idStr = Num.toStr (TypeId.toU64 id)
|
||||
idStr = Num.toStr (typeIDtoU64 id)
|
||||
|
||||
crash "TypeId #$(idStr) was not found in Types. This should never happen, and means there was a bug in `roc glue`. If you have time, please open an issue at <https://github.com/roc-lang/roc/issues>"
|
||||
|
||||
alignment : Types, TypeId -> U32
|
||||
alignment = \@Types types, id ->
|
||||
when List.get types.aligns (TypeId.toU64 id) is
|
||||
when List.get types.aligns (typeIDtoU64 id) is
|
||||
Ok answer -> answer
|
||||
Err OutOfBounds ->
|
||||
idStr = Num.toStr (TypeId.toU64 id)
|
||||
idStr = Num.toStr (typeIDtoU64 id)
|
||||
|
||||
crash "TypeId #$(idStr) was not found in Types. This should never happen, and means there was a bug in `roc glue`. If you have time, please open an issue at <https://github.com/roc-lang/roc/issues>"
|
||||
|
||||
size : Types, TypeId -> U32
|
||||
size = \@Types types, id ->
|
||||
when List.get types.sizes (TypeId.toU64 id) is
|
||||
when List.get types.sizes (typeIDtoU64 id) is
|
||||
Ok answer -> answer
|
||||
Err OutOfBounds ->
|
||||
idStr = Num.toStr (TypeId.toU64 id)
|
||||
idStr = Num.toStr (typeIDtoU64 id)
|
||||
|
||||
crash "TypeId #$(idStr) was not found in Types. This should never happen, and means there was a bug in `roc glue`. If you have time, please open an issue at <https://github.com/roc-lang/roc/issues>"
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "roc_lang_srv"
|
||||
name = "roc_language_server"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
@ -33,50 +33,49 @@ use self::{analysed_doc::ModuleIdToUrl, tokens::Token};
|
||||
|
||||
pub const HIGHLIGHT_TOKENS_LEGEND: &[SemanticTokenType] = Token::LEGEND;
|
||||
|
||||
/// Contains hashmaps of info about all modules that were analyzed
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ModulesInfo {
|
||||
subs: Mutex<HashMap<ModuleId, Subs>>,
|
||||
exposed: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
|
||||
docs: VecMap<ModuleId, ModuleDocumentation>,
|
||||
struct ModulesInfo {
|
||||
subs_by_module: HashMap<ModuleId, Mutex<Subs>>,
|
||||
exposed_by_module: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
|
||||
docs_by_module: HashMap<ModuleId, ModuleDocumentation>,
|
||||
}
|
||||
|
||||
impl ModulesInfo {
|
||||
fn with_subs<F, A>(&self, mod_id: &ModuleId, f: F) -> Option<A>
|
||||
fn with_subs<F, A>(&self, module_id: &ModuleId, f: F) -> Option<A>
|
||||
where
|
||||
F: FnOnce(&mut Subs) -> A,
|
||||
{
|
||||
self.subs.lock().get_mut(mod_id).map(f)
|
||||
let subs = self.subs_by_module.get(module_id)?;
|
||||
Some(f(&mut subs.lock()))
|
||||
}
|
||||
/// Transforms some of the raw data from the analysis into a state that is
|
||||
/// more useful during processes like completion.
|
||||
fn from_analysis(
|
||||
|
||||
fn get_docs(&self, module_id: &ModuleId) -> Option<&ModuleDocumentation> {
|
||||
self.docs_by_module.get(module_id)
|
||||
}
|
||||
|
||||
fn from_loaded_module(
|
||||
exposes: MutMap<ModuleId, Vec<(Symbol, Variable)>>,
|
||||
typechecked: &MutMap<ModuleId, CheckedModule>,
|
||||
docs_by_module: VecMap<ModuleId, ModuleDocumentation>,
|
||||
) -> ModulesInfo {
|
||||
// We wrap this in Arc because later we will go through each module's imports and
|
||||
// store the full list of symbols that each imported module exposes.
|
||||
// example: A imports B. B exposes [add, multiply, divide] and A will store a reference to that list.
|
||||
let exposed = exposes
|
||||
let exposed_by_module = exposes
|
||||
.into_iter()
|
||||
.map(|(module_id, symbols)| (module_id, Arc::new(symbols)))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
// Combine the subs from all modules
|
||||
let all_subs = Mutex::new(
|
||||
typechecked
|
||||
let subs_by_module = typechecked
|
||||
.iter()
|
||||
.map(|(module_id, checked_module)| {
|
||||
(*module_id, checked_module.solved_subs.0.clone())
|
||||
(*module_id, checked_module.solved_subs.0.clone().into())
|
||||
})
|
||||
.collect::<HashMap<_, _>>(),
|
||||
);
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let docs_by_module = docs_by_module.into_iter().collect();
|
||||
|
||||
ModulesInfo {
|
||||
subs: all_subs,
|
||||
exposed,
|
||||
docs: docs_by_module,
|
||||
subs_by_module,
|
||||
exposed_by_module,
|
||||
docs_by_module,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -84,15 +83,14 @@ impl ModulesInfo {
|
||||
#[derive(Debug, Clone)]
|
||||
pub(super) struct AnalyzedModule {
|
||||
exposed_imports: Vec<(Symbol, Variable)>,
|
||||
/// imports are grouped by which module they come from
|
||||
imports: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
|
||||
imports_by_module: HashMap<ModuleId, Arc<Vec<(Symbol, Variable)>>>,
|
||||
module_id: ModuleId,
|
||||
interns: Interns,
|
||||
subs: Subs,
|
||||
abilities: AbilitiesStore,
|
||||
declarations: Declarations,
|
||||
modules_info: Arc<ModulesInfo>,
|
||||
// We need this because ModuleIds are not stable between compilations, so a ModuleId visible to
|
||||
// ModuleIds are not stable between compilations, so a ModuleId visible to
|
||||
// one module may not be true global to the language server.
|
||||
module_id_to_url: ModuleIdToUrl,
|
||||
}
|
||||
@ -164,7 +162,7 @@ pub(crate) fn global_analysis(doc_info: DocInfo) -> Vec<AnalyzedDocument> {
|
||||
|
||||
let exposed_imports = resolve_exposed_imports(exposed_imports, &exposes);
|
||||
|
||||
let modules_info = Arc::new(ModulesInfo::from_analysis(
|
||||
let modules_info = Arc::new(ModulesInfo::from_loaded_module(
|
||||
exposes,
|
||||
&typechecked,
|
||||
docs_by_module,
|
||||
@ -304,7 +302,7 @@ impl<'a> AnalyzedDocumentBuilder<'a> {
|
||||
|
||||
let analyzed_module = AnalyzedModule {
|
||||
exposed_imports,
|
||||
imports,
|
||||
imports_by_module: imports,
|
||||
subs,
|
||||
abilities,
|
||||
declarations,
|
||||
@ -342,7 +340,7 @@ impl<'a> AnalyzedDocumentBuilder<'a> {
|
||||
(
|
||||
id,
|
||||
self.modules_info
|
||||
.exposed
|
||||
.exposed_by_module
|
||||
.get(&id)
|
||||
.unwrap_or(&Arc::new(vec![]))
|
||||
.clone(),
|
@ -69,6 +69,7 @@ impl DocInfo {
|
||||
let end = Position::new(self.line_info.num_lines(), 0);
|
||||
Range::new(start, end)
|
||||
}
|
||||
|
||||
pub fn get_prefix_at_position(&self, position: Position) -> String {
|
||||
let position = position.to_roc_position(&self.line_info);
|
||||
let offset = position.offset as usize;
|
||||
@ -82,6 +83,7 @@ impl DocInfo {
|
||||
|
||||
String::from(symbol)
|
||||
}
|
||||
|
||||
pub fn format(&self) -> Option<Vec<TextEdit>> {
|
||||
let source = &self.source;
|
||||
let arena = &Bump::new();
|
||||
@ -175,10 +177,12 @@ impl AnalyzedDocument {
|
||||
|
||||
let (region, var) = roc_can::traverse::find_closest_type_at(pos, declarations)?;
|
||||
|
||||
//TODO:Can this be integrated into find closest type? is it even worth it?
|
||||
let docs_opt = self
|
||||
.symbol_at(position)
|
||||
.and_then(|symb| modules_info.docs.get(module_id)?.get_doc_for_symbol(&symb));
|
||||
//TODO: Can this be integrated into "find closest type"? Is it worth it?
|
||||
let docs_opt = self.symbol_at(position).and_then(|symbol| {
|
||||
modules_info
|
||||
.get_docs(module_id)?
|
||||
.get_doc_for_symbol(&symbol)
|
||||
});
|
||||
|
||||
let type_str = format_var_type(var, &mut subs.clone(), module_id, interns);
|
||||
|
||||
@ -238,7 +242,7 @@ impl AnalyzedDocument {
|
||||
subs,
|
||||
declarations,
|
||||
exposed_imports,
|
||||
imports,
|
||||
imports_by_module: imports,
|
||||
modules_info,
|
||||
..
|
||||
} = self.module()?;
|
||||
@ -299,7 +303,7 @@ impl AnalyzedDocument {
|
||||
&mut subs.clone(),
|
||||
module_id,
|
||||
interns,
|
||||
modules_info.docs.get(module_id),
|
||||
modules_info.get_docs(module_id),
|
||||
exposed_imports,
|
||||
);
|
||||
Some(completions)
|
@ -77,7 +77,7 @@ pub(super) fn get_module_completion_items(
|
||||
mod_id,
|
||||
interns,
|
||||
exposed_symbols,
|
||||
modules_info.docs.get(mod_id),
|
||||
modules_info.get_docs(mod_id),
|
||||
modules_info,
|
||||
)),
|
||||
..Default::default()
|
||||
@ -90,7 +90,7 @@ pub(super) fn get_module_completion_items(
|
||||
exposed_symbols,
|
||||
modules_info,
|
||||
mod_id,
|
||||
modules_info.docs.get(mod_id),
|
||||
modules_info.get_docs(mod_id),
|
||||
interns,
|
||||
)
|
||||
} else {
|
@ -1,5 +1,5 @@
|
||||
app "args"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [pf.Stdout, pf.Arg, pf.Task.{ Task }]
|
||||
provides [main] to pf
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
app "countdown"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [pf.Stdin, pf.Stdout, pf.Task.{ await, loop }]
|
||||
provides [main] to pf
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
app "echo"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [pf.Stdin, pf.Stdout, pf.Task.{ Task }]
|
||||
provides [main] to pf
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
app "env"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [pf.Stdout, pf.Stderr, pf.Env, pf.Task.{ Task }]
|
||||
provides [main] to pf
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
app "file-io"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [
|
||||
pf.Stdout,
|
||||
pf.Stderr,
|
||||
|
@ -1,5 +1,5 @@
|
||||
app "form"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [pf.Stdin, pf.Stdout, pf.Task.{ await, Task }]
|
||||
provides [main] to pf
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
app "http-get"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [pf.Http, pf.Task.{ Task }, pf.Stdin, pf.Stdout]
|
||||
provides [main] to pf
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
app "ingested-file-bytes"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [
|
||||
pf.Stdout,
|
||||
"../../LICENSE" as license : _, # A type hole can also be used here.
|
||||
|
@ -1,5 +1,5 @@
|
||||
app "ingested-file"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [
|
||||
pf.Stdout,
|
||||
"ingested-file.roc" as ownCode : Str,
|
||||
|
@ -1,5 +1,5 @@
|
||||
app "helloWorld"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [pf.Stdout]
|
||||
provides [main] to pf
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Shows how Roc values can be logged
|
||||
#
|
||||
app "inspect-logging"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [
|
||||
pf.Stdout,
|
||||
Community,
|
||||
|
@ -1,6 +1,6 @@
|
||||
app "example"
|
||||
packages {
|
||||
cli: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br",
|
||||
cli: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br",
|
||||
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br",
|
||||
}
|
||||
imports [
|
||||
|
@ -1,6 +1,6 @@
|
||||
app "example"
|
||||
packages {
|
||||
pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br",
|
||||
pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br",
|
||||
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br",
|
||||
}
|
||||
imports [
|
||||
|
@ -3,7 +3,7 @@ let
|
||||
inherit (compile-deps) zigPkg llvmPkgs llvmVersion llvmMajorMinorStr glibcPath libGccSPath;
|
||||
|
||||
subPackagePath = if subPackage != null then "crates/${subPackage}" else null;
|
||||
mainBin = if subPackage == "lang_srv" then "roc_language_server" else "roc";
|
||||
mainBin = if subPackage == "language_server" then "roc_language_server" else "roc";
|
||||
filteredSource = pkgs.callPackage ./fileFilter.nix { };
|
||||
in
|
||||
rustPlatform.buildRustPackage {
|
||||
|
@ -18,7 +18,7 @@ let
|
||||
|
||||
# all rust crates in workspace.members of Cargo.toml
|
||||
roc-full = callPackage ./builder.nix { };
|
||||
roc-lang-server = callPackage ./builder.nix { subPackage = "lang_srv"; };
|
||||
roc-lang-server = callPackage ./builder.nix { subPackage = "language_server"; };
|
||||
# only the CLI crate = executable provided in nightly releases
|
||||
roc-cli = callPackage ./builder.nix { subPackage = "cli"; };
|
||||
};
|
||||
|
@ -24,19 +24,19 @@ view =
|
||||
Desc [Ident "main", Kw "="] "<p>This defines <code class=\"ident\">main</code>, which is where our program will begin.</p><p>In Roc, <a href=\"/tutorial#https://www.roc-lang.org/tutorial#naming-things\">all definitions are constant</a>, so writing <code class=\"ident\">main =</code> again in the same scope would give an error.</p>",
|
||||
Indent,
|
||||
Desc [Ident "Path.fromStr", Str "\"url.txt\""] "<p>This converts the string <code>\"url.txt\"</code> into a <code>Path</code> by passing it to <code>Path.fromStr</code>.</p><p>Function arguments are separated with whitespace. Parentheses are only needed in <a href=\"/tutorial#calling-functions\">nested function calls</a>.</p>",
|
||||
Indent,
|
||||
Newline,
|
||||
Desc [Kw "|>", Ident "storeEmail"] "<p>The <a href=\"/tutorial#the-pipe-operator\">pipe operator</a> (<code>|></code>) is syntax sugar for passing the previous value to the next function in the \"pipeline.\"</p><p>This line takes the value that <code>Path.fromStr \"url.txt\"</code> returns and passes it to <code>storeEmail</code>.</p><p>The next <code>|></code> continues the pipeline.</p>",
|
||||
Newline,
|
||||
Desc [Kw "|>", Ident "Task.onErr", Ident "handleErr"] "<p>If the task returned by the previous step in the pipeline fails, pass its error to <code>handleErr</code>.</p><p>The pipeline essentially does this:</p><pre><code>a = Path.fromStr \"url.txt\"\nb = storeEmail a\n\nTask.onErr b handleErr</code></pre><p>It creates a <code>Path</code> from a string, passes it to <code>storeEmail</code>, and specifies how to handle errors if storing the email fails.</p>",
|
||||
Outdent,
|
||||
Outdent,
|
||||
Newline,
|
||||
Desc [Ident "storeEmail", Kw "=", Lambda ["path"]] "<p>This <a href=\"/tutorial#defining-functions\">defines a function</a> named <code>storeEmail</code>. It takes one argument, named <code>path</code>.</p><p>In Roc, functions are ordinary values, so we assign names to them using <code>=</code> like with any other value.</p><p>The <code>\\arg1, arg2 -></code> syntax begins a function, and the part after <code>-></code> is the function's body.</p>",
|
||||
Indent,
|
||||
Desc [Ident "url", Kw "=", Ident "File.readUtf8!", Ident "path"] "<p>This passes <code>path</code> to the <code>File.readUtf8</code> function, which reads the contents of the file (as a <a href=\"https://en.wikipedia.org/wiki/UTF-8\">UTF-8</a> string) into <code>url</code>.</p><p>The <code>!</code> operator is similar to <code>await</code> in other languages. It means “wait until the asynchronous <code>File.readUtf8</code> operation successfully completes.”</p><p>If the file read fails (maybe because <code>path</code> refers to a missing file), the rest of this function will be skipped, and the <code>handleErr</code> function will take over.</p>",
|
||||
Newline,
|
||||
Desc [Ident "user", Kw "=", Ident "Http.get!", Ident "url", Ident "Json.format"] "<p>This fetches the contents of the URL and decodes them as <a href=\"https://www.json.org\">JSON</a>.</p><p>If the shape of the JSON isn't compatible with the type of <code>user</code> (based on type inference), this will give a decoding error immediately.</p><p>As with all the other function calls involving the <code>!</code> operator, if there's an error, nothing else in <code>storeEmail</code> will be run, and <code>handleErr</code> will run.</p>",
|
||||
Newline,
|
||||
Desc [Ident "dest", Kw "=", Ident "Path.fromStr", StrInterpolation "\"" "user.name" ".txt\""] "<p>The <code>\$(user.name)</code> in this string literal will be replaced with the value stored in the <code>user</code> record's <code>name</code> field. This is <a href=\"/tutorial#string-interpolation\">string interpolation</a>.</p><p>Note that this function call doesn't involve the <code>!</code> operator. That's because <code>Path.fromStr</code> is a synchronous operation, so there's no need to use <code>!</code> to wait for it to finish.</p>",
|
||||
Desc [Ident "dest", Kw "=", Ident "Path.fromStr", StrInterpolation "\"" "user.name" ".txt\""] "<p>The <code>\$(user.name)</code> in this string literal will be replaced with the value stored in the <code>user</code> record's <code>name</code> field. This is <a href=\"/tutorial#string-interpolation\">string interpolation</a>.</p><p>Note that this function call doesn't involve the <code>!</code> operator. That's because <code>Path.fromStr</code> doesn't involve any Tasks, so there's no need to use <code>!</code> to wait for it to finish.</p>",
|
||||
Newline,
|
||||
Desc [Ident "File.writeUtf8!", Ident "dest", Ident "user.email"] "<p>This writes <code>user.email</code> to the file, encoded as <a href=\"https://en.wikipedia.org/wiki/UTF-8\">UTF-8</a>.</p><p>Since <code>File.writeUtf8</code> doesn't produce any information on success, we don't bother using <code>=</code> like we did on the other lines.</p>",
|
||||
Newline,
|
||||
|
@ -146,12 +146,14 @@ If you would like your organization to become an official sponsor of Roc's devel
|
||||
We'd also like to express our gratitude to our generous [individual sponsors](https://github.com/sponsors/roc-lang/)! A special thanks to those sponsoring $25/month or more:
|
||||
|
||||
<ul id="individual-sponsors">
|
||||
<li><a href="https://github.com/krzysztofgb">Krzysztof G.</a>
|
||||
<li><a href="https://github.com/smores56">Sam Mohr</a>
|
||||
<li><a href="https://github.com/megakilo">Steven Chen</a>
|
||||
<li><a href="https://github.com/asteroidb612">Drew Lazzeri</a>
|
||||
<li><a href="https://github.com/mrmizz">Alex Binaei</a>
|
||||
<li><a href="https://github.com/jonomallanyk">Jono Mallanyk</a>
|
||||
<li><a href="https://github.com/AngeloChecked">Angelo Ceccato</a></li>
|
||||
<li><a href="https://github.com/noverby">Niclas Overby</a></li>
|
||||
<li><a href="https://github.com/krzysztofgb">Krzysztof G.</a></li>
|
||||
<li><a href="https://github.com/smores56">Sam Mohr</a></li>
|
||||
<li><a href="https://github.com/megakilo">Steven Chen</a></li>
|
||||
<li><a href="https://github.com/asteroidb612">Drew Lazzeri</a></li>
|
||||
<li><a href="https://github.com/mrmizz">Alex Binaei</a></li>
|
||||
<li><a href="https://github.com/jonomallanyk">Jono Mallanyk</a></li>
|
||||
<li><a href="https://github.com/chris-packett">Chris Packett</a></li>
|
||||
<li><a href="https://github.com/jamesbirtles">James Birtles</a></li>
|
||||
<li><a href="https://github.com/Ivo-Balbaert">Ivo Balbaert</a></li>
|
||||
|
@ -8,7 +8,7 @@ Here is a Roc application that prints `"Hello, World!"` to the command line:
|
||||
|
||||
```roc
|
||||
app "hello"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [pf.Stdout]
|
||||
provides [main] to pf
|
||||
|
||||
|
@ -155,7 +155,7 @@ Make a file named `main.roc` and put this in it:
|
||||
|
||||
```roc
|
||||
app "hello"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [pf.Stdout]
|
||||
provides [main] to pf
|
||||
|
||||
@ -1425,7 +1425,7 @@ Let's take a closer look at the part of `main.roc` above the `main` def:
|
||||
|
||||
```roc
|
||||
app "hello"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [pf.Stdout]
|
||||
provides [main] to pf
|
||||
```
|
||||
@ -1437,7 +1437,7 @@ The line `app "hello"` shows that this module is a Roc application. The "hello"
|
||||
The remaining lines all involve the [platform](https://github.com/roc-lang/roc/wiki/Roc-concepts-explained#platform) this application is built on:
|
||||
|
||||
```roc
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [pf.Stdout]
|
||||
provides [main] to pf
|
||||
```
|
||||
@ -1536,7 +1536,7 @@ Let's start with a basic "Hello World" program.
|
||||
|
||||
```roc
|
||||
app "cli-tutorial"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [pf.Stdout]
|
||||
provides [main] to pf
|
||||
|
||||
@ -1570,7 +1570,7 @@ Let's change `main` to read a line from `stdin`, and then print what we got:
|
||||
|
||||
```roc
|
||||
app "cli-tutorial"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.0/oKWkaruh2zXxin_xfsYsCJobH1tO8_JvNkFzDwwzNUQ.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.9.1/y_Ww7a2_ZGjp0ZTt9Y_pNdSqqMRdMLzHMKfdN8LWidk.tar.br" }
|
||||
imports [pf.Stdout, pf.Stdin, pf.Task]
|
||||
provides [main] to pf
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user