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
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ Enabled via `processors` config:
| Haskell | `haskell`, `hs`, `lhs` | MicroHs (not GHC) |
| Haskell (Wasm) | `haskell-wasm`, `hs-wasm`, `wasm.hs`, `hswasm` | GHC WASM |
| Rust (Wasm) | `rust-wasm`, `rs-wasm`, `wasm.rs` | Rust WASM |
| Scala (Wasm) | `scala-wasm`, `scala`, `wasm.scala` | Scala 3 compiler + Scala.js linker (WASM) |
| Swift (Wasm) | `swift-wasm`, `swift`, `wasm.swift` | Swift WASM |
| Zig (Wasm) | `zig-wasm`, `zig` | Zig WASM |
| V (Wasm) | `v-wasm`, `v`, `vlang`, `wasm.v`, `vsh` | V compiler in WASM |
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A [feature-rich](https://livecodes.io/docs/features/), open-source, **client-sid
[![LiveCodes: npm version](https://img.shields.io/npm/v/livecodes)](https://www.npmjs.com/package/livecodes)
[![LiveCodes: npm downloads](https://img.shields.io/npm/dm/livecodes)](https://www.npmjs.com/package/livecodes)
[![LiveCodes: jsdelivr downloads](https://data.jsdelivr.com/v1/package/npm/livecodes/badge?style=rounded)](https://www.jsdelivr.com/package/npm/livecodes)
[![LiveCodes: languages](https://img.shields.io/badge/languages-126-blue)](https://livecodes.io/docs/languages/)
[![LiveCodes: languages](https://img.shields.io/badge/languages-127-blue)](https://livecodes.io/docs/languages/)
[![LiveCodes: docs](https://img.shields.io/badge/Documentation-575757?logo=gitbook&logoColor=white)](https://livecodes.io/docs/)
[![LiveCodes: llms.txt](https://img.shields.io/badge/llms.txt-575757?logo=googledocs&logoColor=white)](https://livecodes.io/docs/llms.txt)
[![LiveCodes: llms-full.txt](https://img.shields.io/badge/llms--full.txt-575757?logo=googledocs&logoColor=white)](https://livecodes.io/docs/llms-full.txt)
Expand Down
145 changes: 145 additions & 0 deletions docs/docs/languages/scala-wasm.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Scala (Wasm)

[Scala](https://www.scala-lang.org/) is a statically typed language that combines functional and
object-oriented programming. Scala 3 programs can target the JVM, JavaScript (via
[Scala.js](https://www.scala-js.org/)) and native platforms.

In LiveCodes, Scala source is compiled and linked **in the browser** by the Scala 3 compiler and the
Scala.js linker, both compiled to WebAssembly. The program then runs in the
[result page](../features/result.mdx). Nothing is sent to a server.

:::info Note

The toolchain is about 46 MB and is downloaded on the first run, which can take some time. Macros
work but download a second compiler and about 23 MB of additional compiler IR the first time they
are used.

:::

## Usage

Demo:

import LiveCodes from '../../src/components/LiveCodes.tsx';

export const scalaConfig = {
activeEditor: 'script',
script: {
language: 'scala-wasm',
content: `@main def hello(): Unit =
val squares = (1 to 5).map(n => n * n)
println("squares: " + squares.mkString(", "))
println("sum: " + squares.sum)
`,
},
mode: 'simple',
editor: 'auto',
tools: {
status: 'full',
},
};

<LiveCodes config={scalaConfig}></LiveCodes>

### Standard I/O

Scala code runs in the context of the result page. Standard output (e.g. `println`) is captured and
displayed in the [console](../features/console.mdx). Standard input is passed by setting the
`livecodes.scala.input` property in JavaScript.

The input is a one-shot buffer: once it is exhausted, `readLine()` returns `null` and the typed
`StdIn` readers throw `EOFException`. It is not an interactive terminal. The following read from the
supplied buffer:

- `scala.io.StdIn.readLine()` and the typed readers (`readInt`, `readDouble`, ...)
- `scala.Console.in`
- `System.in.read()` and `BufferedReader(new InputStreamReader(System.in))`

`java.util.Scanner` and `scala.io.Source.stdin` have no working Scala.js implementation, and a
parameterized `@main` method cannot be wrapped (use an object main or a parameterless `@main`).

### Communication with JavaScript

A few helper properties and methods are available in the browser global `livecodes.scala` object:

- `livecodes.scala.input`: The standard input passed to the Scala program. It can be set before the
initial run, or passed to `run` for subsequent runs.
- `livecodes.scala.loaded`: A promise that resolves when the Scala environment is ready (and rejects
if it fails to load). Other helpers should be used after this promise resolves.
- `livecodes.scala.ready`: A boolean that is `true` once the environment is loaded and a run has
completed.
- `livecodes.scala.output`: The standard output of the last run.
- `livecodes.scala.error`: The compiler diagnostics or runtime error of the last run, if any.
- `livecodes.scala.exitCode`: The exit code of the last run.
- `livecodes.scala.run(input)`: Runs the code in the editor, optionally passing standard input, and
returns a promise that resolves to `{ output, error, exitCode }`.

```js
await livecodes.scala.loaded;
const { output, error, exitCode } = await livecodes.scala.run(livecodes.scala.input);
console.log(output);
```

## Language Info

### Name

`scala-wasm`

### Aliases / Extensions

`scala`, `scala-wasm`, `wasm.scala`

### Editor

`script`

## Compiler

The [Scala 3 compiler](https://www.scala-lang.org/) and the [Scala.js](https://www.scala-js.org/) linker compiled to WebAssembly,
provided by [`scala-toolchain-wasm`](https://github.com/tothambrus11/scala-toolchain-wasm) and
packaged for LiveCodes by
[`@live-codes/scala-wasm`](https://github.com/live-codes/scala-wasm).

### Version

Scala 3.8.3, Scala.js 1.20.2

## Code Formatting

Not supported.

## Limitations

- `java.util.Scanner` and `scala.io.Source.stdin` are not available.
- A parameterized `@main` method cannot read the supplied standard input.

## Example Usage

This example passes standard input and runs the code again on each click, using the browser global
`livecodes.scala` (also check the code in the HTML editor):

<LiveCodes template="scala" height="80vh"></LiveCodes>

## Live Reload

By default, new code changes are sent to the result page for re-evaluation without a full page
reload, avoiding the need to reinitialize the Scala WebAssembly environment. This behavior can be
disabled by adding the code comment `// __livecodes_reload__` to the Scala code, which forces a full
page reload.

This comment can be added in the
[`hiddenContent` property of the editor](../configuration/configuration-object.mdx#markup) for
embedded playgrounds.

## Starter Template

https://livecodes.io/?template=scala

## Links

- [Scala](https://www.scala-lang.org/)
- [Scala 3 documentation](https://docs.scala-lang.org/)
- [Scala.js](https://www.scala-js.org/)
- [scala-toolchain-wasm](https://github.com/tothambrus11/scala-toolchain-wasm)
- [@live-codes/scala-wasm](https://github.com/live-codes/scala-wasm)
1 change: 1 addition & 0 deletions docs/src/components/LanguageSliders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export default function Sliders() {
{ name: 'flutter', title: 'Flutter' },
{ name: 'v-wasm', title: 'V (Wasm)' },
{ name: 'rust-wasm', title: 'Rust (Wasm)' },
{ name: 'scala-wasm', title: 'Scala (Wasm)' },
{ name: 'swift-wasm', title: 'Swift (Wasm)' },
{ name: 'haskell', title: 'Haskell' },
{ name: 'haskell-wasm', title: 'Haskell (Wasm)' },
Expand Down
1 change: 1 addition & 0 deletions docs/src/components/TemplateList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const templates = [
{ name: 'cpp-wasm', title: 'C++ (Wasm) Starter', thumbnail: 'cpp.svg' },
{ name: 'objc-wasm', title: 'Objective-C (Wasm) Starter', thumbnail: 'objective-c.svg' },
{ name: 'rust-wasm', title: 'Rust (Wasm) Starter', thumbnail: 'rust.svg' },
{ name: 'scala', title: 'Scala Starter', thumbnail: 'scala.svg' },
{ name: 'swift-wasm', title: 'Swift (Wasm) Starter', thumbnail: 'swift.svg' },
{ name: 'haskell', title: 'Haskell Starter', thumbnail: 'haskell.svg' },
{ name: 'haskell-wasm', title: 'Haskell (Wasm) Starter', thumbnail: 'haskell.svg' },
Expand Down
24 changes: 24 additions & 0 deletions e2e/specs/compilers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1677,6 +1677,30 @@
);
});

test('Scala (Wasm)', async ({ page, getTestUrl, editor }) => {

Check failure on line 1680 in e2e/specs/compilers.spec.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add at least one assertion to this test case.

See more on https://sonarcloud.io/project/issues?id=live-codes_livecodes&issues=AaFguDeYvvXsIHng7Nua&open=AaFguDeYvvXsIHng7Nua&pullRequest=1077
test.skip(editor === 'codejar', 'please fix');
test.slow(); // the engine downloads the toolchain and compiles WebAssembly

const code = `@main def hello(): Unit =
println("Hello from Scala")
`;

// Code is passed in the URL (rather than typed) so the first run cannot race the editor.
await page.goto(getTestUrl({ tools: 'console|open', active: 'script', 'scala-wasm': code }));

const { app, waitForResultUpdate } = await getLoadedApp(page);
await waitForResultUpdate();

await app.waitForFunction(
() =>
[...document.querySelectorAll('#console-container .luna-console-log-item')].some((item) =>
(item.textContent ?? '').includes('Hello from Scala'),
),
undefined,
{ timeout: 180_000 },
);
});

test('Ruby', async ({ page, getTestUrl, editor }) => {
test.skip(editor === 'codejar', 'please fix');

Expand Down
28 changes: 28 additions & 0 deletions e2e/specs/starter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,34 @@ test.describe('Starter Templates from UI', () => {
await expect(getResult().locator('text=You clicked')).toHaveText('You clicked 3 times.');
});

test('scala Starter', async ({ page, getTestUrl }) => {
// the compiler and the standard library are downloaded on the first run
test.slow();

await page.goto(getTestUrl());

const { app, getResult, waitForResultUpdate } = await getLoadedApp(page);

await app.click('[aria-label="Project"]');
await app.click('text=New');
await app.click('text=Scala Starter');

await waitForEditorFocus(app);
await waitForResultUpdate();

// the counter stays disabled until the runtime has loaded and run once
await expect(getResult().locator('#counter-button')).toBeEnabled({ timeout: 280_000 });

await getResult().click('text=Click me');
await getResult().click('text=Click me');
await getResult().click('text=Click me');

await expect(getResult().locator('h1')).toHaveText('Hello, Scala!');
// Each click triggers an asynchronous run, so the counter is asserted with
// retries rather than read once.
await expect(getResult().locator('text=You clicked')).toHaveText('You clicked 3 times.');
});

test('swift-wasm Starter', async ({ page, getTestUrl }) => {
// the Swift compiler, the linker and the stdlib sysroot (~70 MB) are downloaded on
// the first run
Expand Down
1 change: 1 addition & 0 deletions functions/vendors/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const starterTemplates = {
"d-wasm": "D Starter",
"v-wasm": "V Starter",
"rust-wasm": "Rust (Wasm) Starter",
"scala": "Scala Starter",
"swift-wasm": "Swift (Wasm) Starter",
"zig-wasm": "Zig (Wasm) Starter",
"java": "Java Starter",
Expand Down
1 change: 1 addition & 0 deletions scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ const iifeBuild = () =>
'languages/commonlisp/lang-commonlisp-script.ts',
'languages/java-8/lang-java-8-script.ts',
'languages/java-wasm/lang-java-wasm-script.ts',
'languages/scala-wasm/lang-scala-wasm-script.ts',
'languages/cpp/lang-cpp-script.ts',
'languages/clang-wasm/lang-clang-wasm-script.ts',
'languages/nim/lang-nim-wasm-script.ts',
Expand Down
1 change: 1 addition & 0 deletions server/php/inc/starter-templates.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"d-wasm": "D Starter",
"v-wasm": "V Starter",
"rust-wasm": "Rust (Wasm) Starter",
"scala": "Scala Starter",
"swift-wasm": "Swift (Wasm) Starter",
"zig-wasm": "Zig (Wasm) Starter",
"java": "Java Starter",
Expand Down
1 change: 1 addition & 0 deletions src/livecodes/UI/command-menu-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ export const getCommandMenuActions = ({
'odin-wasm',
'v-wasm',
'rust-wasm',
'scala',
'swift-wasm',
'zig-wasm',
'java',
Expand Down
1 change: 1 addition & 0 deletions src/livecodes/assets/templates/scala.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions src/livecodes/html/language-info.html
Original file line number Diff line number Diff line change
Expand Up @@ -2647,6 +2647,38 @@ <h3 data-i18n="language-info:sass.name">Sass</h3>
</li>
</ul>
</section>
<section data-lang="scala-wasm">
<h3 data-i18n="language-info:scalaWasm.name">Scala (Wasm)</h3>
<div data-i18n="language-info:scalaWasm.desc" data-i18n-prop="innerHTML">
Scala 3 is compiled and linked in the browser by the Scala 3 compiler and the Scala.js linker,
both running on WebAssembly. Nothing is sent to a server.
</div>
<ul data-i18n="language-info:scalaWasm.link" data-i18n-prop="innerHTML">
<li>
<a href="https://www.scala-lang.org/" target="_blank" rel="noopener"
>Scala official website</a
>
</li>
<li>
<a href="https://docs.scala-lang.org/" target="_blank" rel="noopener">Scala documentation</a>
</li>
<li>
<a href="https://www.scala-js.org/" target="_blank" rel="noopener"
>Scala.js official website</a
>
</li>
<li>
<a href="{{DOCS_BASE_URL}}languages/scala-wasm" target="_blank" rel="noopener"
>LiveCodes Documentation</a
>
</li>
<li>
<a href="?template=scala" class="button" target="_parent" data-template="scala"
>Load starter template</a
>
</li>
</ul>
</section>
<section data-lang="scheme">
<h3 data-i18n="language-info:scheme.name">Scheme</h3>
<div data-i18n="language-info:scheme.desc">Scheme running in the browser using biwascheme.</div>
Expand Down
9 changes: 9 additions & 0 deletions src/livecodes/i18n/locales/ar/language-info.lokalise.json
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,15 @@
"sass.name": {
"translation": "Sass"
},
"scalaWasm.desc": {
"translation": "يتم تجميع Scala 3 وربطه في المتصفح بواسطة مُترجم Scala 3 ورابط Scala.js، وكلاهما يعمل على WebAssembly. لا يُرسَل أي شيء إلى خادم."
},
"scalaWasm.link": {
"translation": "<tag-1> <tag-2>الموقع الرسمي لـ Scala</tag-2> </tag-1> <tag-3> <tag-4>وثائق Scala</tag-4> </tag-3> <tag-5> <tag-6>الموقع الرسمي لـ Scala.js</tag-6> </tag-5> <tag-7> <tag-8>وثائق LiveCodes</tag-8> </tag-7> <tag-9> <tag-10>تحميل قالب البدء</tag-10> </tag-9>"
},
"scalaWasm.name": {
"translation": "Scala (Wasm)"
},
"scheme.desc": {
"translation": "Scheme تعمل في المتصفح باستخدام biwascheme."
},
Expand Down
5 changes: 5 additions & 0 deletions src/livecodes/i18n/locales/ar/language-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>الموقع الرسمي لـ Sass</2> </1> <3> <4>وثائق Sass</4> </3> <5> <6>بناء جملة Sass (المسافة البادئة)</6> </5> <7> <8>تعلم X في Y دقيقة، حيث X=sass</8> </7>',
name: 'Sass',
},
scalaWasm: {
desc: 'يتم تجميع Scala 3 وربطه في المتصفح بواسطة مُترجم Scala 3 ورابط Scala.js، وكلاهما يعمل على WebAssembly. لا يُرسَل أي شيء إلى خادم.',
link: '<1> <2>الموقع الرسمي لـ Scala</2> </1> <3> <4>وثائق Scala</4> </3> <5> <6>الموقع الرسمي لـ Scala.js</6> </5> <7> <8>وثائق LiveCodes</8> </7> <9> <10>تحميل قالب البدء</10> </9>',
name: 'Scala (Wasm)',
},
scheme: {
desc: 'Scheme تعمل في المتصفح باستخدام biwascheme.',
link: '<1> <2>لغة البرمجة Scheme</2> </1> <3> <4>الموقع الرسمي لـ BiwaScheme</4> </3> <5> <6>مرجع BiwaScheme</6> </5> <7> <8>تحميل قالب البداية</8> </7>',
Expand Down
3 changes: 3 additions & 0 deletions src/livecodes/i18n/locales/ar/translation.lokalise.json
Original file line number Diff line number Diff line change
Expand Up @@ -2160,6 +2160,9 @@
"templates.starter.rust-wasm": {
"translation": "قالب Rust (Wasm)"
},
"templates.starter.scala": {
"translation": "قالب Scala"
},
"templates.starter.scheme": {
"translation": "قالب Scheme"
},
Expand Down
1 change: 1 addition & 0 deletions src/livecodes/i18n/locales/ar/translation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,7 @@ const translation: I18nTranslation = {
ruby: 'قالب Ruby',
'ruby-wasm': 'قالب Ruby-Wasm',
'rust-wasm': 'قالب Rust (Wasm)',
scala: 'قالب Scala',
scheme: 'قالب Scheme',
shadcnui: 'قالب shadcn/ui',
solid: 'قالب Solid',
Expand Down
9 changes: 9 additions & 0 deletions src/livecodes/i18n/locales/bn/language-info.lokalise.json
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,15 @@
"sass.name": {
"translation": "Sass"
},
"scalaWasm.desc": {
"translation": "Scala 3 ব্রাউজারে Scala 3 কম্পাইলার এবং Scala.js লিঙ্কার দ্বারা কম্পাইল ও লিঙ্ক করা হয়, উভয়ই WebAssembly-তে চলে। কোনো সার্ভারে কিছু পাঠানো হয় না।"
},
"scalaWasm.link": {
"translation": "<tag-1> <tag-2>Scala অফিসিয়াল ওয়েবসাইট</tag-2> </tag-1> <tag-3> <tag-4>Scala ডকুমেন্টেশন</tag-4> </tag-3> <tag-5> <tag-6>Scala.js অফিসিয়াল ওয়েবসাইট</tag-6> </tag-5> <tag-7> <tag-8>লাইভকোড ডকুমেন্টেশন</tag-8> </tag-7> <tag-9> <tag-10>স্টার্টার টেমপ্লেট লোড করুন</tag-10> </tag-9>"
},
"scalaWasm.name": {
"translation": "Scala (Wasm)"
},
"scheme.desc": {
"translation": "biwascheme ব্যবহার করে ব্রাউজারে চলমান Scheme।"
},
Expand Down
Loading
Loading