diff --git a/_includes/footer.html b/_includes/footer.html index b00049db89..82d0ba252d 100644 --- a/_includes/footer.html +++ b/_includes/footer.html @@ -53,6 +53,9 @@ + + + diff --git a/_overviews/scala3-book/first-look-at-types.md b/_overviews/scala3-book/first-look-at-types.md index 4a822d6edf..1a795c8922 100644 --- a/_overviews/scala3-book/first-look-at-types.md +++ b/_overviews/scala3-book/first-look-at-types.md @@ -58,6 +58,7 @@ val list: List[Any] = List( "a string", 732, // an integer 'c', // a character + '\'', // a character with a backslash escape true, // a boolean value () => "an anonymous function returning a string" ) @@ -76,6 +77,7 @@ Here’s the output of the program: a string 732 c +' true ``` @@ -119,7 +121,22 @@ In your code you can also append the characters `L`, `D`, and `F` (and their low ```scala val x = 1_000L // val x: Long = 1000 val y = 2.2D // val y: Double = 2.2 -val z = 3.3F // val z: Float = 3.3 +val z = -3.3F // val z: Float = -3.3 +``` + +You may also use hexadecimal notation to format integer numbers (normally `Int`, but which also support the +`L` suffix to specify that they are `Long`): + +```scala +val a = 0xACE // val a: Int = 2766 +val b = 0xfd_3aL // val b: Long = 64826 +``` + +Scala supports many different ways to format the same floating point number, e.g. +```scala +val q = .25 // val q: Double = 0.25 +val r = 2.5e-1 // val r: Double = 0.25 +val s = .0025e2F // val s: Float = 0.25 ``` {% endtab %} {% endtabs %} diff --git a/resources/js/functions.js b/resources/js/functions.js index 8dd9e9ec76..b0b0dd6d68 100644 --- a/resources/js/functions.js +++ b/resources/js/functions.js @@ -67,6 +67,7 @@ $(document).ready(function() { hljs.configure({ languages: ["scala", "bash"] }) + hljs.registerLanguage("scala", highlightDotty); hljs.highlightAll(); }); @@ -431,7 +432,7 @@ $(document).ready(function() { function setupTabs(tabs, namespace, defaultValue) { const PreferenceStorage = Storage('org.scala-lang.docs.preferences'); const preferredValue = PreferenceStorage.getPreference(namespace, defaultValue); - + activateTab(tabs, preferredValue) // setup listeners to record new preferred Scala version. diff --git a/resources/js/hljs-scala3.js b/resources/js/hljs-scala3.js new file mode 100644 index 0000000000..0ba80087ef --- /dev/null +++ b/resources/js/hljs-scala3.js @@ -0,0 +1,476 @@ +function highlightDotty(hljs) { + + // identifiers + const capitalizedId = /\b[A-Z][$\w]*\b/ + const alphaId = /[a-zA-Z$_][$\w]*/ + const op1 = /[^\s\w\d,;"'()[\]{}=:]/ + const op2 = /[^\s\w\d,;"'()[\]{}]/ + const compound = `[a-zA-Z$][a-zA-Z0-9$]*_${op2.source}` // e.g. value_= + const id = new RegExp(`(${compound}|${alphaId.source}|${op2.source}{2,}|${op1.source}+|\`.+?\`)`) + + // numbers + const hexDigit = '[a-fA-F0-9]' + const hexNumber = `0[xX](${hexDigit})+(_(${hexDigit})+)*[lL]?` + const wholePart = `(\\d+)(_\\d+)*` + const decPoint = `\\.\\d+` + const floatingSuffix = `([eE][-+]?\\d+)?[fFdD]?` + const intOrFloat = `${decPoint}${floatingSuffix}|\\b${wholePart}([lLfFdD]|(${decPoint})?${floatingSuffix})` + const number = new RegExp(`(-?)((\\b(${hexNumber})|${intOrFloat}))`) + + // Regular Keywords + // The "soft" keywords (e.g. 'using') are added later where necessary + const alwaysKeywords = { + $pattern: /(\w+|\?=>|\?{1,3}|=>>|=>|<:|>:|_|#|<-|\.nn)/, + keyword: + 'abstract case catch class def do else enum export extends final finally for given ' + + 'if implicit import lazy match new object package private protected override return ' + + 'sealed then throw trait true try type val var while with yield =>> => ?=> <: >: _ ? <- #', + literal: 'true false null this super', + built_in: '??? asInstanceOf isInstanceOf assert implicitly locally summon valueOf .nn' + } + const modifiers = 'abstract|final|implicit|override|private|protected|sealed' + + // End of class, enum, etc. header + const templateDeclEnd = /(\/[/*]|{|:(?= *\n)|\n(?! *(extends|with|derives)))/ + + // all the keywords + soft keywords, separated by spaces + function withSoftKeywords(kwd) { + return { + $pattern: alwaysKeywords.$pattern, + keyword: kwd + ' ' + alwaysKeywords.keyword, + literal: alwaysKeywords.literal, + built_in: alwaysKeywords.built_in + } + } + + // title inside of a complex token made of several parts, but colored as a type (e.g. class) + const TP_TITLE = { + className: 'type', + begin: id, + returnEnd: true, + keywords: alwaysKeywords.keyword, + literal: alwaysKeywords.literal, + built_in: alwaysKeywords.built_in + } + + // title inside of a complex token made of several parts (e.g. class) + const TITLE = { + className: 'title', + begin: id, + returnEnd: true, + keywords: alwaysKeywords.keyword, + literal: alwaysKeywords.literal, + built_in: alwaysKeywords.built_in + } + + // title that goes to the end of a simple token (e.g. val) + const TITLE2 = { + className: 'title', + begin: id, + excludeEnd: true, + endsWithParent: true + } + + const TYPED = { + begin: /: (?=[a-zA-Z()?])/, + end: /\/\/|\/\*|\n/, + endsWithParent: true, + returnEnd: true, + contains: [ + { + // works better than the usual way of defining keyword, + // in this specific situation + className: 'keyword', + begin: /\?\=>|=>>|[=:][><]|\?/, + }, + { + className: 'type', + begin: alphaId + } + ] + } + + const PROBABLY_TYPE = { + className: 'type', + begin: capitalizedId, + relevance: 0 + } + + const NUMBER = { + className: 'number', + begin: number, + relevance: 0, + } + + const CHAR = { + className: 'string', + begin: /\'([^'\\]|\\[\s\S])\'/, + relevance: 0, + } + + // type parameters within [square brackets] + const TPARAMS = { + begin: /\[/, end: /\]/, + keywords: { + $pattern: /<:|>:|[+-?_:]/, + keyword: '<: >: : + - ? _' + }, + contains: [ + hljs.C_BLOCK_COMMENT_MODE, + { + className: 'type', + begin: alphaId + }, + ], + relevance: 3 + } + + // Class or method parameters declaration + const PARAMS = { + className: 'params', + begin: /\(/, end: /\)/, + excludeBegin: true, + excludeEnd: true, + keywords: withSoftKeywords('inline using'), + contains: [ + hljs.C_BLOCK_COMMENT_MODE, + hljs.QUOTE_STRING_MODE, + PROBABLY_TYPE + ] + } + + // (using T1, T2, T3) + const CTX_PARAMS = { + className: 'params', + begin: /\(using (?!\w+:)/, end: /\)/, + excludeBegin: false, + excludeEnd: true, + relevance: 5, + keywords: withSoftKeywords('using'), + contains: [ + PROBABLY_TYPE + ] + } + + // String interpolation + const SUBST = { + className: 'subst', + variants: [ + { begin: /\$[a-zA-Z_]\w*/ }, + { + begin: /\${/, end: /}/, + contains: [ + NUMBER, + hljs.QUOTE_STRING_MODE + ] + } + ] + } + + // "string" or """string""", with or without interpolation + const STRING = { + className: 'string', + variants: [ + hljs.QUOTE_STRING_MODE, + { + begin: '"""', end: '"""', + contains: [hljs.BACKSLASH_ESCAPE], + relevance: 10 + }, + { + begin: alphaId.source + '"', end: '"', + contains: [hljs.BACKSLASH_ESCAPE, SUBST], + illegal: /\n/, + relevance: 5 + }, + { + begin: alphaId.source + '"""', end: '"""', + contains: [hljs.BACKSLASH_ESCAPE, SUBST], + relevance: 10 + } + ] + } + + // Class or method apply + const APPLY = { + begin: /\(/, end: /\)/, + excludeBegin: true, excludeEnd: true, + keywords: { + $pattern: alwaysKeywords.$pattern, + keyword: 'using ' + alwaysKeywords.keyword, + literal: alwaysKeywords.literal, + built_in: alwaysKeywords.built_in + }, + contains: [ + STRING, + NUMBER, + CHAR, + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE, + PROBABLY_TYPE, + ] + } + + // @annot(...) or @my.package.annot(...) + const ANNOTATION = { + className: 'meta', + begin: `@${id.source}(\\.${id.source})*`, + contains: [ + APPLY, + hljs.C_BLOCK_COMMENT_MODE + ] + } + + // Documentation + const SCALADOC = hljs.COMMENT('/\\*\\*', '\\*/', { + contains: [ + { + className: 'doctag', + begin: /@[a-zA-Z]+/ + }, + // markdown syntax elements: + { + className: 'code', + variants: [ + { begin: /```.*\n/, end: /```/ }, + { begin: /`/, end: /`/ } + ], + }, + { + className: 'bold', + variants: [ + { begin: /\*\*/, end: /\*\*/ }, + { begin: /__/, end: /__/ } + ], + }, + { + className: 'emphasis', + variants: [ + { begin: /\*(?!([\*\s/])|([^\*]*\*[\*/]))/, end: /\*/ }, + { begin: /_/, end: /_/ } + ], + }, + { + className: 'bullet', // list item + begin: /- (?=\S)/, end: /\s/, + }, + { + begin: /\[.*?\]\(/, end: /\)/, + contains: [ + { + // mark as "link" only the URL + className: 'link', + begin: /.*?/, + endsWithParent: true + } + ] + } + ] + }) + + // Methods + const METHOD = { + className: 'function', + begin: `((${modifiers}|transparent|inline|infix) +)*def `, end: / =\s|\n/, + excludeEnd: true, + relevance: 5, + keywords: withSoftKeywords('inline infix transparent'), + contains: [ + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE, + TPARAMS, + CTX_PARAMS, + PARAMS, + TYPED, // prevents the ":" (declared type) to become a title + PROBABLY_TYPE, + TITLE + ] + } + + // Variables & Constants + const VAL = { + beginKeywords: 'val var', end: /[=:;\n/]/, + excludeEnd: true, + contains: [ + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE, + TITLE2 + ] + } + + // Type declarations + const TYPEDEF = { + className: 'typedef', + begin: `((${modifiers}|opaque) +)*type `, end: /[=;\n]| ?[<>]:/, + excludeEnd: true, + keywords: withSoftKeywords('opaque'), + contains: [ + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE, + PROBABLY_TYPE, + TITLE, + ] + } + + // Given instances + const GIVEN = { + begin: `((${modifiers}|transparent|inline) +)*given `, end: / =|[=;\n]/, + excludeEnd: true, + keywords: withSoftKeywords('inline transparent given using with'), + contains: [ + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE, + PARAMS, + CTX_PARAMS, + PROBABLY_TYPE, + TITLE + ] + } + + // Extension methods + const EXTENSION = { + begin: /extension/, end: /(\n|def)/, + returnEnd: true, + keywords: 'extension implicit using', + contains: [ + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE, + CTX_PARAMS, + PARAMS, + PROBABLY_TYPE + ] + } + + // 'end' soft keyword + const END = { + begin: `end(?= (if|while|for|match|try|given|extension|this|val|${id.source})\\n)`, end: /\s/, + keywords: 'end' + } + + // Classes, traits, enums, etc. + const EXTENDS_PARENT = { + begin: ' extends ', end: /( with | derives |\/[/*])/, + endsWithParent: true, + returnEnd: true, + keywords: 'extends', + contains: [APPLY, PROBABLY_TYPE] + } + const WITH_MIXIN = { + begin: ' with ', end: / derives |\/[/*]/, + endsWithParent: true, + returnEnd: true, + keywords: 'with', + contains: [APPLY, PROBABLY_TYPE], + relevance: 10 + } + const DERIVES_TYPECLASS = { + begin: ' derives ', end: /\n|\/[/*]/, + endsWithParent: true, + returnEnd: true, + keywords: 'derives', + contains: [PROBABLY_TYPE], + relevance: 10 + } + + const CLASS = { + className: 'class', + begin: `((${modifiers}|open|case|transparent) +)*(class|trait|enum|object|package object)`, end: templateDeclEnd, + keywords: withSoftKeywords('open transparent'), + excludeEnd: true, + contains: [ + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE, + TPARAMS, + CTX_PARAMS, + PARAMS, + EXTENDS_PARENT, + WITH_MIXIN, + DERIVES_TYPECLASS, + TITLE, + PROBABLY_TYPE + ] + } + + // package declaration with a content + const PACKAGE = { + className: 'package', + begin: /package (?=\w+ *[:{\n])/, end: /[:{\n]/, + excludeEnd: true, + keywords: alwaysKeywords, + contains: [ + TITLE + ] + } + + // Case in enum + const ENUM_CASE = { + begin: /case (?!.*=>)/, end: /\n/, + keywords: 'case', + excludeEnd: true, + contains: [ + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE, + PARAMS, + EXTENDS_PARENT, + WITH_MIXIN, + DERIVES_TYPECLASS, + TP_TITLE, + PROBABLY_TYPE + ] + } + + // Case in pattern matching + const MATCH_CASE = { + begin: /case/, end: /=>|\n/, + keywords: 'case', + excludeEnd: true, + contains: [ + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE, + { + begin: /[@_]/, + keywords: { + $pattern: /[@_]/, + keyword: '@ _' + } + }, + NUMBER, + STRING, + PROBABLY_TYPE + ] + } + + // inline someVar[andMaybeTypeParams] match + const INLINE_MATCH = { + begin: /inline [^\n:]+ match/, + keywords: 'inline match' + } + + return { + name: 'Scala3', + aliases: ['scala', 'dotty'], + keywords: alwaysKeywords, + contains: [ + NUMBER, + CHAR, + STRING, + SCALADOC, + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE, + METHOD, + VAL, + TYPEDEF, + PACKAGE, + CLASS, + GIVEN, + EXTENSION, + ANNOTATION, + ENUM_CASE, + MATCH_CASE, + INLINE_MATCH, + END, + APPLY, + PROBABLY_TYPE + ] + } +}