diff --git a/_overviews/contribute/add-guides.md b/_overviews/contribute/add-guides.md
index f99c81de92..2d91c642dc 100644
--- a/_overviews/contribute/add-guides.md
+++ b/_overviews/contribute/add-guides.md
@@ -61,6 +61,28 @@ on [docs.scala-lang.org][home].
And a paragraph, with a [link](https://www.scala-lang.org).
+Tables of contents will be automatically generated in a sidebar for your document, and syntax highlighting
+is provided.
+
+### Criteria for Docs to be Accepted
+
+The goal of this documentation repository is to be highly curated, rather than the approach by other community-driven
+documentation platforms, like wikis. Therefore, to be included on [docs.scala-lang.org][home], a document must:
+
+- **"fit in"** to the repository (_i.e.,_ it should not be a complete duplicate of another article),
+- **be polished**, i.e. it must be thorough, complete, correct, and organized; written as an article to be understood
+ by many users.
+- **be maintained**, if the document might require revisions from time to time, be prepared to keep it up to date, or
+nominate someone to take ownership.
+
+If you have something you're thinking about contributing, or that you're thinking about writing in order to contribute
+-- we'd love to consider it! Please don't hesitate to use GitHub issues and pull requests and the
+`#scala-contributors` room [on Discord](https://discord.com/invite/scala) for any questions, concerns,
+clarifications, etc.
+
+## Code blocks
+
+It's common for various kinds of documents to require code examples.
You can contribute code in a markdown document by either
- in-line by putting backticks around it,
- surrounding by triple backticks,
@@ -79,24 +101,93 @@ indented example:
case class Foo(x: Int)
~~~
-Tables of contents will be automatically generated in a sidebar for your document, and syntax highlighting
-is provided.
+### Scala 2 vs Scala 3
-### Criteria for Docs to be Accepted
+Sometimes you would like to compare between Scala 2 and Scala 3 in a document, for example in
+our [Hello World][hello-world] chapter of the Scala Book. Here is an example of how you
+can generate the same tabs in markdown with the `tabs` directive and class `tabs-scala-version`:
-The goal of this documentation repository is to be highly curated, rather than the approach by other community-driven
-documentation platforms, like wikis. Therefore, to be included on [docs.scala-lang.org][home], a document must:
+
+~~~liquid
+{% tabs hello-world-demo class=tabs-scala-version %}
-- **"fit in"** to the repository (_i.e.,_ it should not be a complete duplicate of another article),
-- **be polished**, i.e. it must be thorough, complete, correct, and organized; written as an article to be understood
- by many users.
-- **be maintained**, if the document might require revisions from time to time, be prepared to keep it up to date, or
-nominate someone to take ownership.
+{% tab 'Scala 2' for=hello-world-demo %}
+```scala
+object hello extends App {
+ println("Hello, World!")
+}
+```
+{% endtab %}
-If you have something you're thinking about contributing, or that you're thinking about writing in order to contribute
--- we'd love to consider it! Please don't hesitate to use GitHub issues and pull requests and the
-`#scala-contributors` room [on Discord](https://discord.com/invite/scala) for any questions, concerns,
-clarifications, etc.
+{% tab 'Scala 3' for=hello-world-demo %}
+```scala
+@main def hello() = println("Hello, World!")
+```
+{% endtab %}
+
+{% endtabs %}
+~~~
+
+
+It is crucial that you use the `tabs-scala-version` class to benefit from some cool user interactions:
+- all other Scala version tabs on the same page will also switch to current tab, whenever one is changed.
+- the tab picked will be remembered accross the site, and when the user returns to the page after some time.
+
+### Typechecked Examples
+
+The site build process uses [mdoc](https://scalameta.org/mdoc/) to typecheck
+code snippets in markdown. This is a great way to ensure the code snippets that
+you're including typecheck and are valid. Here are a few quick tips to get
+started:
+
+First, add `mdoc` after `scala` when you are creating a
+code block. The `mdoc` modifier here will make sure that `mdoc` runs the code
+snippet and ensures that it's valid.
+
+
+
+
+ ```scala mdoc
+val a = 1
+```
+
+If you have a snippet that you expect to fail, you can also account for this by
+using `mdoc:fail` for a compile error `mdoc:crash` for a runtime-error.
+
+
+
+Keep in mind that a single file is all compiled as a single unit, so you can't
+redefine a variable that was defined above in another code snippet. _However_
+there are a couple ways to get around this. Firstly, you can use the `mdoc:nest`
+modifier with will wrap the snippet in a `scala.Predef.locally{...}`. This will
+essentially "hide" the snippet from the others. Another way around this is to
+use the `mdoc:reset` modifier, which _resets_ and forgets about everything up
+above. Here is an example using the various modifiers.
+
+
```scala mdoc:nest
+caseclassFoo(a: Int) // conflicts with Foo above, but it's nested so it's fine
+```
+
+
```scala mdoc
+val a = s"The time is ${now()}"// still have access to the now method from above
+```
+
+
```scala mdoc:reset
+caseclassFoo(a: String) // forget the previous Foo's and start fresh
+```
+
+
```scala mdoc
+val myFoo = Foo("hi") // now we only have access to the last Foo
+```
## Document Templates
@@ -197,64 +288,9 @@ you should use the following format:
|---------|---------|
| content | more |
-### Code blocks
-
-The site build process uses [mdoc](https://scalameta.org/mdoc/) to typecheck
-code snippets in markdown. This is a great way to ensure the code snippets that
-you're including typecheck and are valid. Here are a few quick tips to get
-started:
-
-First, add `mdoc` after `scala` when you are creating a
-code block. The `mdoc` modifier here will make sure that `mdoc` runs the code
-snippet and ensures that it's valid.
-
-
-
-
- ```scala mdoc
-val a = 1
-```
-
-If you have a snippet that you expect to fail, you can also account for this by
-using `mdoc:fail` for a compile error `mdoc:crash` for a runtime-error.
-
-
-
-Keep in mind that a single file is all compiled as a single unit, so you can't
-redefine a variable that was defined above in another code snippet. _However_
-there are a couple ways to get around this. Firstly, you can use the `mdoc:nest`
-modifier with will wrap the snippet in a `scala.Predef.locally{...}`. This will
-essentially "hide" the snippet from the others. Another way around this is to
-use the `mdoc:reset` modifier, which _resets_ and forgets about everything up
-above. Here is an example using the various modifiers.
-
-
```scala mdoc:nest
-caseclassFoo(a: Int) // conflicts with Foo above, but it's nested so it's fine
-```
-
-
```scala mdoc
-val a = s"The time is ${now()}"// still have access to the now method from above
-```
-
-
```scala mdoc:reset
-caseclassFoo(a: String) // forget the previous Foo's and start fresh
-```
-
-
```scala mdoc
-val myFoo = Foo("hi") // now we only have access to the last Foo
-```
-
[collections-overview]: {% link _overviews/collections-2.13/introduction.md %}
[why-contribute]: {% link contribute.md %}
[home]: {% link index.md %}
[overviews-index]: {% link _overviews/index.md %}
[scala-3-reference]: {{ site.scala3ref }}
+[hello-world]: {% link _overviews/scala3-book/taste-hello-world.md %}
diff --git a/_overviews/scala3-book/taste-hello-world.md b/_overviews/scala3-book/taste-hello-world.md
index 5b9cb962ef..4a65bfb0bd 100644
--- a/_overviews/scala3-book/taste-hello-world.md
+++ b/_overviews/scala3-book/taste-hello-world.md
@@ -7,18 +7,45 @@ previous-page: taste-intro
next-page: taste-repl
---
+> **Hint**: in the following examples try picking your preferred Scala version.
+>
+
## Your First Scala Program
-A Scala 3 “Hello, world!” example goes as follows.
+
+A Scala “Hello, World!” example goes as follows.
First, put this code in a file named _hello.scala_:
+
+
+{% tabs hello-world-demo class=tabs-scala-version %}
+
+{% tab 'Scala 2' for=hello-world-demo %}
+```scala
+object hello {
+ def main(args: Array[String]) = {
+ println("Hello, World!")
+ }
+}
+```
+> In this code, we defined a method named `main`, inside a Scala `object` named `hello`.
+> An `object` in Scala is similar to a `class`, but defines a singleton instance that you can pass around.
+> `main` takes an input parameter named `args` that must be typed as `Array[String]`, (ignore `args` for now).
+
+{% endtab %}
+
+{% tab 'Scala 3' for=hello-world-demo %}
```scala
-@main def hello() = println("Hello, world!")
+@main def hello() = println("Hello, World!")
```
+> In this code, `hello` is a method.
+> It’s defined with `def`, and declared to be a “main” method with the `@main` annotation.
+> It prints the `"Hello, World!"` string to standard output (STDOUT) using the `println` method.
+
+{% endtab %}
-In this code, `hello` is a method.
-It’s defined with `def`, and declared to be a “main” method with the `@main` annotation.
-It prints the `"Hello, world!"` string to standard output (STDOUT) using the `println` method.
+{% endtabs %}
+
Next, compile the code with `scalac`:
@@ -28,6 +55,19 @@ $ scalac hello.scala
If you’re coming to Scala from Java, `scalac` is just like `javac`, so that command creates several files:
+
+{% tabs hello-world-outputs class=tabs-scala-version %}
+
+{% tab 'Scala 2' for=hello-world-outputs %}
+```bash
+$ ls -1
+hello$.class
+hello.class
+hello.scala
+```
+{% endtab %}
+
+{% tab 'Scala 3' for=hello-world-outputs %}
```bash
$ ls -1
hello$package$.class
@@ -37,6 +77,10 @@ hello.scala
hello.class
hello.tasty
```
+{% endtab %}
+
+{% endtabs %}
+
Like Java, the _.class_ files are bytecode files, and they’re ready to run in the JVM.
@@ -44,7 +88,7 @@ Now you can run the `hello` method with the `scala` command:
```bash
$ scala hello
-Hello, world!
+Hello, World!
```
Assuming that worked, congratulations, you just compiled and ran your first Scala application.
@@ -64,6 +108,27 @@ import scala.io.StdIn.readLine
To demonstrate how this works, let’s create a little example. Put this source code in a file named _helloInteractive.scala_:
+
+{% tabs hello-world-interactive class=tabs-scala-version %}
+
+{% tab 'Scala 2' for=hello-world-interactive %}
+```scala
+import scala.io.StdIn.readLine
+
+object helloInteractive {
+
+ def main(args: Array[String]) = {
+ println("Please enter your name:")
+ val name = readLine()
+
+ println("Hello, " + name + "!")
+ }
+
+}
+```
+{% endtab %}
+
+{% tab 'Scala 3' for=hello-world-interactive %}
```scala
import scala.io.StdIn.readLine
@@ -73,6 +138,10 @@ import scala.io.StdIn.readLine
println("Hello, " + name + "!")
```
+{% endtab %}
+
+{% endtabs %}
+
In this code we save the result of `readLine` to a variable called `name`, we then
use the `+` operator on strings to join `"Hello, "` with `name` and `"!"`, making one single string value.
diff --git a/_plugins/jekyll-tabs-lib/template.erb b/_plugins/jekyll-tabs-lib/template.erb
index 0d2e2ec147..e9d7867102 100644
--- a/_plugins/jekyll-tabs-lib/template.erb
+++ b/_plugins/jekyll-tabs-lib/template.erb
@@ -4,7 +4,7 @@
>
@@ -12,7 +12,7 @@
<% environment["tabs-#{@name}"].each do | tab | %>
-
+
<% end %>
diff --git a/_sass/components/tab.scss b/_sass/components/tab.scss
index 4d21d60c5e..8207f3eb52 100755
--- a/_sass/components/tab.scss
+++ b/_sass/components/tab.scss
@@ -93,10 +93,8 @@
}
.tabsection {
- border-bottom: $base-border-gray;
- border-left: $base-border-gray;
- border-right: $base-border-gray;
- border-radius: $border-radius-medium;
+ padding: 0.1px 0; // ensure inner content is correctly padded
+ border-left: 2px solid $base-border-color-gray;
.nav-tab {
padding-left: 0;
diff --git a/resources/js/functions.js b/resources/js/functions.js
index 7bd39db251..08b87a2312 100644
--- a/resources/js/functions.js
+++ b/resources/js/functions.js
@@ -382,6 +382,86 @@ $(document).ready(function() {
}
});
+// Browser Storage Support (https://stackoverflow.com/a/41462752/2538602)
+function storageAvailable(type) {
+ try {
+ var storage = window[type],
+ x = '__storage_test__';
+ storage.setItem(x, x);
+ storage.removeItem(x);
+ return true;
+ }
+ catch (e) {
+ return false;
+ }
+}
+
+// Store preference for Scala 2 vs 3
+$(document).ready(function() {
+
+ const Storage = (namespace) => {
+ return ({
+ getPreference(key, defaultValue) {
+ const res = localStorage.getItem(`${namespace}.${key}`);
+ return res === null ? defaultValue : res;
+ },
+ setPreference(key, value, onChange) {
+ const old = this.getPreference(key, null);
+ if (old !== value) { // activate effect only if value changed.
+ localStorage.setItem(`${namespace}.${key}`, value);
+ onChange(old);
+ }
+ }
+ });
+ };
+
+ /** Links all tabs created in Liquid templates with class ".tabs-scala-version"
+ * on the page together, such that
+ * changing a tab to `Scala 2` will activate all other tab sections to
+ * also change to "Scala 2".
+ * Also records a preference for the Scala version in localStorage, so
+ * that when the page is refreshed, the same tab will be selected.
+ * On page load, selects the tab corresponding to stored Scala version.
+ */
+ function setupScalaVersionTabs(scalaVersionTabs) {
+ const DocsPreferences = Storage('org.scala-lang.docs.preferences');
+ const Scala3 = 'scala-3';
+ const scalaVersion = DocsPreferences.getPreference('scalaVersion', Scala3);
+
+ function activateTab(tabs, scalaVersion) {
+ // click the code tab corresponding to the preferred Scala version.
+ tabs.find('input[data-target=' + scalaVersion + ']').prop("checked", true);
+ }
+
+ activateTab(scalaVersionTabs, scalaVersion);
+
+ // setup listeners to record new preferred Scala version.
+ scalaVersionTabs.find('input').on('change', function() {
+ // if checked then set the preferred version, and activate the other tabs on page.
+ if ($(this).is(':checked')) {
+ const parent = $(this).parent();
+ const scalaVersion = $(this).data('target');
+
+ DocsPreferences.setPreference('scalaVersion', scalaVersion, oldValue => {
+ // when we set a new scalaVersion, find scalaVersionTabs except current one
+ // and activate those tabs.
+ activateTab(scalaVersionTabs.not(parent), scalaVersion);
+ });
+
+ }
+
+ });
+ }
+
+ if (storageAvailable('localStorage')) {
+ var scalaVersionTabs = $(".tabsection.tabs-scala-version");
+ if (scalaVersionTabs.length) {
+ setupScalaVersionTabs(scalaVersionTabs);
+ }
+ }
+
+});
+
// OS detection
function getOS() {
var osname = "linux";