From 835ca9b34dae298592f823740939af539bc45f6d Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 22 Jun 2022 17:31:50 +0200 Subject: [PATCH 1/7] allow multiple tab sections with same labels --- _plugins/jekyll-tabs-lib/template.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 @@ From 736822e521047345d487846affe43e5eaf82712a Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 22 Jun 2022 18:20:40 +0200 Subject: [PATCH 2/7] make hello world page language agnostic --- _overviews/scala3-book/taste-hello-world.md | 81 +++++++++++++++++++-- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/_overviews/scala3-book/taste-hello-world.md b/_overviews/scala3-book/taste-hello-world.md index 5b9cb962ef..b6aab6cb7e 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 it 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. From 40f3fd6886256e0c7a1ee62e868c0c647052f528 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 30 Jun 2022 12:55:39 +0200 Subject: [PATCH 3/7] add JS to remember language preference --- resources/js/functions.js | 72 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/resources/js/functions.js b/resources/js/functions.js index 7bd39db251..8a3264562f 100644 --- a/resources/js/functions.js +++ b/resources/js/functions.js @@ -382,6 +382,78 @@ $(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 ? 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); + } + } + }); + }; + + function setupScalaVersionTabs(scalaVersionTabs) { + const BookStorage = Storage('org.scala-lang.docs.scala3.book'); + const Scala3 = 'scala-3'; + const scalaVersion = BookStorage.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'); + + BookStorage.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"; From 616cd101608dc22f87926ca6d5587db90d0fd8da Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 1 Jul 2022 11:27:25 +0200 Subject: [PATCH 4/7] address comments --- _overviews/scala3-book/taste-hello-world.md | 2 +- _sass/components/tab.scss | 6 ++++++ resources/js/functions.js | 10 +++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/_overviews/scala3-book/taste-hello-world.md b/_overviews/scala3-book/taste-hello-world.md index b6aab6cb7e..4a65bfb0bd 100644 --- a/_overviews/scala3-book/taste-hello-world.md +++ b/_overviews/scala3-book/taste-hello-world.md @@ -30,7 +30,7 @@ object hello { ``` > 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 it for now). +> `main` takes an input parameter named `args` that must be typed as `Array[String]`, (ignore `args` for now). {% endtab %} diff --git a/_sass/components/tab.scss b/_sass/components/tab.scss index 4d21d60c5e..a7bca3532e 100755 --- a/_sass/components/tab.scss +++ b/_sass/components/tab.scss @@ -122,6 +122,12 @@ left: -999em; } } + + blockquote { + border: none; + border-left: 4px solid $base-border-color-gray; + padding: 4px 20px; + } } .tabsection>input:nth-child(1):checked~.tabcontent section:nth-child(1), diff --git a/resources/js/functions.js b/resources/js/functions.js index 8a3264562f..bc09e3f00a 100644 --- a/resources/js/functions.js +++ b/resources/js/functions.js @@ -403,7 +403,7 @@ $(document).ready(function() { return ({ getPreference(key, defaultValue) { const res = localStorage.getItem(`${namespace}.${key}`); - return !res ? defaultValue : res; + return res === null ? defaultValue : res; }, setPreference(key, value, onChange) { const old = this.getPreference(key, null); @@ -415,6 +415,14 @@ $(document).ready(function() { }); }; + /** 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 BookStorage = Storage('org.scala-lang.docs.scala3.book'); const Scala3 = 'scala-3'; From 47d2a9049ff427b0a3b16da7e6508af0d7b84e2c Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 1 Jul 2022 12:17:58 +0200 Subject: [PATCH 5/7] new tab design --- _sass/components/tab.scss | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/_sass/components/tab.scss b/_sass/components/tab.scss index a7bca3532e..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; @@ -122,12 +120,6 @@ left: -999em; } } - - blockquote { - border: none; - border-left: 4px solid $base-border-color-gray; - padding: 4px 20px; - } } .tabsection>input:nth-child(1):checked~.tabcontent section:nth-child(1), From b0ed2df843d3fed96ff08a5315c3edaa7c472045 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 1 Jul 2022 14:50:02 +0200 Subject: [PATCH 6/7] more generic storage key --- resources/js/functions.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/js/functions.js b/resources/js/functions.js index bc09e3f00a..08b87a2312 100644 --- a/resources/js/functions.js +++ b/resources/js/functions.js @@ -424,9 +424,9 @@ $(document).ready(function() { * On page load, selects the tab corresponding to stored Scala version. */ function setupScalaVersionTabs(scalaVersionTabs) { - const BookStorage = Storage('org.scala-lang.docs.scala3.book'); + const DocsPreferences = Storage('org.scala-lang.docs.preferences'); const Scala3 = 'scala-3'; - const scalaVersion = BookStorage.getPreference('scalaVersion', Scala3); + const scalaVersion = DocsPreferences.getPreference('scalaVersion', Scala3); function activateTab(tabs, scalaVersion) { // click the code tab corresponding to the preferred Scala version. @@ -442,7 +442,7 @@ $(document).ready(function() { const parent = $(this).parent(); const scalaVersion = $(this).data('target'); - BookStorage.setPreference('scalaVersion', scalaVersion, oldValue => { + 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); From 0b34ba61a5d7d257922b3bb719d4187f8f4dd046 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 1 Jul 2022 15:19:52 +0200 Subject: [PATCH 7/7] document scala version tabs --- _overviews/contribute/add-guides.md | 176 +++++++++++++++++----------- 1 file changed, 106 insertions(+), 70 deletions(-) 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. + +
```scala mdoc:fail
+val b: String = 3 // won't compile
+```
+ +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
+import java.time.Instant
+
+def now() = Instant.now()
+object Foo {}
+```
+ +
```scala mdoc:nest
+case class Foo(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
+case class Foo(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. - -
```scala mdoc:fail
-val b: String = 3 // won't compile
-```
- -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
-import java.time.Instant
-
-def now() = Instant.now()
-object Foo {}
-```
- -
```scala mdoc:nest
-case class Foo(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
-case class Foo(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 %}