Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ crate-type = ["staticlib"]
# Note: we use "=version" for reproducability, cargo may silently use a more recent version if you do not use `=`.
[dependencies]
libc = "=0.2.180" # keep version in sync with libc workspace dep below
crossterm = "=0.29.0"
roc_std_new.workspace = true
roc_io_error.workspace = true
roc_random.workspace = true
roc_command.workspace = true
memoffset = "=0.9.1"

[target.'cfg(not(target_os = "macos"))'.dependencies]
sys-locale.workspace = true

[workspace]
members = [
"crates/roc_io_error",
Expand Down
1 change: 1 addition & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ get_rust_triple() {
esac
}


# All supported targets
ALL_TARGETS="x64mac arm64mac x64musl arm64musl"

Expand Down
10 changes: 10 additions & 0 deletions ci/all_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ export PATH="$(pwd)/$ROC_DIR:$PATH"
echo ""
echo "Using roc version: $(roc version)"

if [ "$(uname -s)" = "Darwin" ] && [ -z "${SDKROOT:-}" ]; then
SDKROOT=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null || true)
if [ -n "$SDKROOT" ]; then
export SDKROOT
echo "Using SDKROOT: $SDKROOT"
fi
fi

# Build the platform
if [ "${NO_BUILD:-}" != "1" ]; then
echo ""
Expand All @@ -105,6 +113,8 @@ MIGRATED_EXAMPLES=(
"command"
"time"
"random"
"locale"
"tty"
)

EXAMPLES_DIR="${ROOT_DIR}/examples/"
Expand Down
7 changes: 3 additions & 4 deletions ci/expect_scripts/locale.exp
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ source ./ci/expect_scripts/shared-code.exp
spawn $env(EXAMPLES_DIR)locale

set expected_output [normalize_output {
The most preferred locale for this system or application: [a-zA-Z\-]+
The most preferred locale for this system or application: [A-Za-z0-9_\-]+
All available locales for this system or application: \[.*\]
}]

expect -re $expected_output {
expect eof {
check_exit_and_segfault
}
expect eof {
check_exit_and_segfault
}
}

Expand Down
21 changes: 21 additions & 0 deletions ci/expect_scripts/tty.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/expect

# uncomment line below for debugging
# exp_internal 1

set timeout 7

source ./ci/expect_scripts/shared-code.exp

spawn $env(EXAMPLES_DIR)tty

expect -re "Tty: enabling raw mode" {
expect -re "Tty: disabling raw mode" {
expect eof {
check_exit_and_segfault
}
}
}

puts stderr "\nExpect script failed: output was not as expected. Diff the output with expected_output in this script. Alternatively, uncomment `exp_internal 1` to debug."
exit 1
2 changes: 2 additions & 0 deletions crates/roc_env/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ version.workspace = true
[dependencies]
roc_std.workspace = true
roc_file.workspace = true

[target.'cfg(not(target_os = "macos"))'.dependencies]
sys-locale.workspace = true
42 changes: 42 additions & 0 deletions crates/roc_env/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,55 @@ pub fn exe_path() -> RocResult<RocList<u8>, ()> {
}
}

#[cfg(target_os = "macos")]
fn locale_from_env() -> Option<String> {
for key in ["LC_ALL", "LC_CTYPE", "LANG"] {
if let Ok(value) = std::env::var(key) {
let trimmed = value.trim();
if trimmed.is_empty() {
continue;
}
let locale = trimmed
.split('.')
.next()
.unwrap_or(trimmed)
.split('@')
.next()
.unwrap_or(trimmed)
.trim();
if !locale.is_empty() {
return Some(locale.to_string());
}
}
}

None
}

#[cfg(target_os = "macos")]
pub fn get_locale() -> RocResult<RocStr, ()> {
locale_from_env()
.map(|locale| RocResult::ok(locale.as_str().into()))
.unwrap_or_else(|| RocResult::err(()))
}

#[cfg(target_os = "macos")]
pub fn get_locales() -> RocList<RocStr> {
match locale_from_env() {
Some(locale) => RocList::from_slice(&[RocStr::from(locale.as_str())]),
None => RocList::empty(),
}
}

#[cfg(not(target_os = "macos"))]
pub fn get_locale() -> RocResult<RocStr, ()> {
sys_locale::get_locale().map_or_else(
|| RocResult::err(()),
|locale| RocResult::ok(locale.to_string().as_str().into()),
)
}

#[cfg(not(target_os = "macos"))]
pub fn get_locales() -> RocList<RocStr> {
const DEFAULT_MAX_LOCALES: usize = 10;
let locales = sys_locale::get_locales();
Expand Down
1 change: 0 additions & 1 deletion crates/roc_host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ path = "src/lib.rs"
crossterm.workspace = true
memmap2.workspace = true
memchr.workspace = true
sys-locale.workspace = true
libc.workspace = true
backtrace.workspace = true
roc_std.workspace = true
Expand Down
5 changes: 3 additions & 2 deletions examples/locale.roc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ main! = |_args| {
Stdout.line!("The most preferred locale for this system or application: ${locale_str}")

all_locales = Locale.all!()
Stdout.line!("All available locales: ${Inspect.to_str(all_locales)}")
locales_str = Str.join_with(all_locales, ", ")
Stdout.line!("All available locales for this system or application: [${locales_str}]")

Ok({})
}
}
2 changes: 1 addition & 1 deletion examples/random.roc
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ main! = |_args| {
Err(Exit(1))
}
}
}
}
8 changes: 4 additions & 4 deletions examples/terminal-app-snake.roc
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ init_snake_len = len(initial_state.snake_lst)

main! : List Arg => Result {} _
main! = |_args|
Tty.enable_raw_mode!({})
Tty.enable_raw_mode!()

game_loop!(initial_state)?

Tty.disable_raw_mode!({})
Tty.disable_raw_mode!()
Stdout.line!("\n--- Game Over ---")

game_loop! : GameState => Result {} _
Expand Down Expand Up @@ -111,7 +111,7 @@ move_head = |head, direction|

draw_game! : GameState => Result {} _
draw_game! = |state|
clear_screen!({})?
clear_screen!()?

Stdout.line!("\nControls: W A S D to move, Q to quit\n\r")?

Expand Down Expand Up @@ -148,7 +148,7 @@ draw_game_pure = |state|
)
|> Str.join_with("\r\n")

clear_screen! = |{}|
clear_screen! = |()|
Stdout.write!("\u(001b)[2J\u(001b)[H") # ANSI escape codes to clear screen

# NonEmptyList helpers
Expand Down
17 changes: 17 additions & 0 deletions examples/tty.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
app [main!] { pf: platform "../platform/main.roc" }

import pf.Stdout
import pf.Tty

## Raw mode allows you to change the behaviour of the terminal.
## This is useful for running an app like vim or a game in the terminal.

main! = |_args| {
Stdout.line!("Tty: enabling raw mode")
Tty.enable_raw_mode!()

Stdout.line!("Tty: disabling raw mode")
Tty.disable_raw_mode!()

Ok({})
}
11 changes: 11 additions & 0 deletions platform/Locale.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Locale := [].{
## Returns the most preferred locale for the system or application.
##
## The returned [Str] is a BCP 47 language tag, like `en-US` or `fr-CA`.
get! : () => Str

## Returns the preferred locales for the system or application.
##
## The returned [Str] are BCP 47 language tags, like `en-US` or `fr-CA`.
all! : () => List(Str)
}
14 changes: 14 additions & 0 deletions platform/Tty.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Provides functionality to change the behaviour of the terminal.
## This is useful for running an app like vim or a game in the terminal.
Tty := [].{
## Enable terminal [raw mode](https://en.wikipedia.org/wiki/Terminal_mode) to disable some default terminal bevahiour.
##
## This leads to the following changes:
## - Input will not be echoed to the terminal screen.
## - Input will be sent straight to the program instead of being buffered (= collected) until the Enter key is pressed.
## - Special keys like Backspace and CTRL+C will not be processed by the terminal driver but will be passed to the program.
enable_raw_mode! : () => {}

## Revert terminal to default behaviour
disable_raw_mode! : () => {}
}
4 changes: 3 additions & 1 deletion platform/main.roc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
platform ""
requires {} { main! : List(Str) => Try({}, [Exit(I32), ..]) }
exposes [Cmd, Dir, Env, File, Path, Random, Sleep, Stdin, Stdout, Stderr, Utc]
exposes [Cmd, Dir, Env, File, Locale, Path, Random, Sleep, Stdin, Stdout, Stderr, Tty, Utc]
packages {}
provides { main_for_host! : "main_for_host" }
targets: {
Expand All @@ -17,12 +17,14 @@ import Cmd
import Dir
import Env
import File
import Locale
import Path
import Random
import Sleep
import Stdin
import Stdout
import Stderr
import Tty
import Utc

main_for_host! : List(Str) => I32
Expand Down
Loading