diff --git a/Scala/Dedentation Rules - case.tmPreferences b/Scala/Dedentation Rules - case.tmPreferences
deleted file mode 100644
index c79e433432..0000000000
--- a/Scala/Dedentation Rules - case.tmPreferences
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- scope
-
- source.scala meta.block.case.non-first - comment
- settings
-
- decreaseIndentPattern
- (?x)
- ^ (.*\*/)? \s* \} .* $ # curly brace dedent (has to be recreated)
- | ^ (.*\*/)? \s* \) .* $ # paren dedent (has to be recreated)
- | ^ \s* \b(case)\b .* $ # any subsequent case expressions are dedents AND indents
-
-
-
-
diff --git a/Scala/Indentation Rules.tmPreferences b/Scala/Indentation Rules.tmPreferences
index 0279995e92..dc54e9308e 100644
--- a/Scala/Indentation Rules.tmPreferences
+++ b/Scala/Indentation Rules.tmPreferences
@@ -9,23 +9,28 @@
(?x)
^ .* \{ [^}"']* $ # curly brace indent
| ^ .* \( [^)"']* $ # paren indent
+
+ # if a line ends with `=`, then it's a line-wrapped declaration (e.g. val x = \n)
+ | ^ .* = \s* $
+
+ # if a line ends with `:`, then it's a line-wrapped declaration (e.g. class Foo: \n)
+ | ^ .* : \s* $
+
+ # attempts to detect a line-wrapped control construct without curly braces (e.g. if (foo) \n)
+ | ^ .* \b(if|do|while|for|match|catch|try|else|yield)\b .* \) \s* $
+
+ # any line that starts with extension is an extension now
+ | ^ \s* extension\b .* $
+
+ # case statements have always been braceless, now they can just happen anywhere
+ | ^ \s* \b(case)\b .* => .* $
decreaseIndentPattern
(?x)
^ (.*\*/)? \s* \} .* $ # curly brace dedent
| ^ (.*\*/)? \s* \) .* $ # parent dedent
-
- bracketIndentNextLinePattern
- (?x)
- # if a line ends with `=`, then it's a line-wrapped declaration (e.g. val x = \n)
- ^ .* = \s* $
-
- # attempts to detect a line-wrapped control construct without curly braces (e.g. if (foo) \n)
- | ^ .* \b(if|do|while|for)\b .* \) \s* $
-
- # simpler line-wrapped control constructs
- | ^ .* \b(else)\b \s* $
- | ^ .* \b(yield)\b \s* $
+ | ^ \s* end ($|\s+ .* $) # end token
+ | ^ \s* (catch|else|yield) ($|\s+ .* $) # braceless things (I would prefer to also do while/do but we can't)
diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax
index d9759d9bd2..e31b901dd5 100644
--- a/Scala/Scala.sublime-syntax
+++ b/Scala/Scala.sublime-syntax
@@ -16,7 +16,8 @@ first_line_match: |-
variables:
# all reserved words (https://www.safaribooksonline.com/library/view/learning-scala/9781449368814/apa.html)
- keywords: '\b(?:abstract|case|catch|class|def|do|else|extends|false|final|finally|for|forSome|if|implicit|import|lazy|match|new|null|object|override|package|private|protected|return|sealed|super|this|throw|trait|true|try|type|val|var|while|with|yield)\b'
+ # note that this omits conditionally reserved words
+ keywords: '\b(?:abstract|case|catch|class|def|do|else|enum|extends|false|final|finally|for|forSome|given|if|implicit|import|inline|lazy|match|new|null|object|opaque|override|package|private|protected|return|sealed|super|this|throw|trait|transparent|true|try|type|val|var|while|with|yield)\b'
# lookaround for operators
nonopchar: '[[[:alpha:]]0-9\s\(\)\[\]\{\}'']'
@@ -70,12 +71,14 @@ variables:
typeid: '(?:{{typeplainid}}|`[^`\n]+`)'
alphaid: (?:{{upper}}{{idrest}}|{{varid}})
# Custom productions
- rightarrow: '=>|\x{21D2}'
+ rightarrow: '=>(?!>)|\x{21D2}'
upperid: '(?:(\b\p{Lu}|\$){{idrest}})'
typeprefix: '(:)\s*'
# hack to cover up to three levels of nested parentheses
withinparens: '(?:\((?:[^\(\)]|\((?:[^\(\)]|\([^\(\)]*\))*\))*\))'
withinbrackets: '(?:\[(?:[^\[\]]|\[(?:[^\[\]]|\[[^\[\]]*\])*\])*\])'
+ lambdalookahead: '({{idorunder}}|{{withinparens}})\s*(?:{{rightarrow}})(?:/\*|//|[[[:alpha:]]0-9\s\)\]\}])'
+ lambdalookaheadtyped: '({{idorunder}}|{{idorunder}}\s*:(\s*{{id}}\s*{{withinbrackets}}?(\s*[\.#]\s*{{id}}\s*{{withinbrackets}}?)*)+|{{idorunder}}\s*:\s*{{withinparens}}|{{withinparens}})\s*(?:{{rightarrow}})(?:/\*|//|[[[:alpha:]]0-9\s\)\]\}])'
# This is the full XML Name production, but should not be used where namespaces
# are possible. Those locations should use a qualified_name.
xml_name: '[[:alpha:]:_][[:alnum:]:_.-]*'
@@ -108,6 +111,7 @@ contexts:
- include: storage-modifiers
- include: declarations
- include: for-comprehension
+ - include: if-statement
- include: keywords
- include: imports
- include: strings
@@ -157,21 +161,27 @@ contexts:
scope: constant.character.literal.scala
comments:
- - match: (//).*$
+ - match: '(//).*$\n?'
scope: comment.line.double-slash.scala
captures:
1: punctuation.definition.comment.scala
- ascription-no-function:
+ ascription-no-function-no-or:
- match: '{{typeprefix}}'
captures:
1: punctuation.ascription.scala
push:
- match: '(?={{rightarrow}})'
pop: true
- - include: single-type-expression-no-function
+ - include: single-type-expression-no-function-no-or
ascription:
+ # in Scala 3, this can be either an ascription *or* a section, and we don't know for sure
+ # also, Scala allows this syntax to follow any identifier, so we can't be as precise as the Python mode
+ - match: ':(?=\s*$)'
+ scope: punctuation.section.begin.scala
+ - match: ':(?=\s*{{lambdalookahead}})'
+ scope: punctuation.section.begin.scala
- match: '{{typeprefix}}'
captures:
1: punctuation.ascription.scala
@@ -229,23 +239,43 @@ contexts:
pop: true
lambdas:
- # lambda lookahead
- - match: '(?=({{idorunder}}|{{idorunder}}\s*:(\s*{{id}}\s*{{withinbrackets}}?(\s*[\.#]\s*{{id}}\s*{{withinbrackets}}?)*)+|{{idorunder}}\s*:\s*{{withinparens}}|{{withinparens}})\s*(?:{{rightarrow}})(?:/\*|//|[[[:alpha:]]0-9\s\)\]\}]))'
+ # lambda lookahead with braces (allows for types)
+ - match: '\{(?=\s*{{lambdalookaheadtyped}})'
+ scope: punctuation.section.block.begin.scala
push:
+ - meta_scope: meta.block.scala
+ - match: '\}'
+ scope: punctuation.section.block.end.scala
+ pop: true
- match: '{{rightarrow}}'
scope: keyword.declaration.function.arrow.lambda.scala
- pop: true
+ set:
+ - meta_scope: meta.block.scala
+ - match: '\}'
+ scope: punctuation.section.block.end.scala
+ pop: true
+ - include: main
- include: lambda-declaration
+ # lambda lookahead without braces (disallows types)
+ - match: '(?={{lambdalookahead}})'
+ push: lambda-init
+ lambda-init:
+ - match: '{{rightarrow}}'
+ scope: keyword.declaration.function.arrow.lambda.scala
+ pop: true
+ - include: lambda-declaration
lambda-declaration-base:
- - match: \(
+ - match: '\('
scope: punctuation.section.group.begin.scala
push:
- - match: \)
+ - match: '\)'
scope: punctuation.section.group.end.scala
pop: true
- include: lambda-declaration-parens
- match: '{{id}}'
scope: variable.parameter.scala
+ - match: ','
+ scope: punctuation.separator.scala
- match: '_'
scope: variable.language.underscore.scala
lambda-declaration-parens:
@@ -273,13 +303,12 @@ contexts:
base-types:
- match: \b(?:Unit|Boolean|Byte|Char|Short|Int|Float|Long|Double|Any|AnyRef|AnyVal|Nothing)\b
scope: storage.type.primitive.scala
- literal-constants:
+
+ literal-constants-base:
- match: \bfalse\b
scope: constant.language.boolean.false.scala
- match: \btrue\b
scope: constant.language.boolean.true.scala
- - match: \bnull\b
- scope: constant.language.null.scala
# TODO negation
# source: http://www.scala-lang.org/files/archive/spec/2.11/01-lexical-syntax.html#floating-point-literals
- match: |-
@@ -311,8 +340,14 @@ contexts:
captures:
1: constant.numeric.value.scala
2: constant.numeric.suffix.scala
+
+ literal-constants:
+ - include: literal-constants-base
+ - match: \bnull\b
+ scope: constant.language.null.scala
- match: \(\)
scope: constant.language.scala
+
base-constants:
- include: literal-constants
- match: \b(?:this|super)\b
@@ -355,6 +390,11 @@ contexts:
1: keyword.declaration.function.scala
2: entity.name.function.scala
push: function-type-parameter-list
+ - match: '\b(enum)(?:\s+({{id}}))'
+ captures:
+ 1: keyword.declaration.enum.scala
+ 2: entity.name.enum.scala
+ push: class-type-parameter-list
- match: '\b(?:(case)\s+)?(class|trait|object)(?:\s+({{id}}))'
scope: meta.class.identifier.scala
captures:
@@ -362,6 +402,9 @@ contexts:
2: keyword.declaration.class.scala
3: entity.name.class.scala
push: class-type-parameter-list
+ - match: '(?=\bcase\b)'
+ branch_point: case
+ branch: [case-decl, case-pattern]
- match: '\b(type)\s+({{id}})'
captures:
1: storage.type.scala
@@ -416,55 +459,140 @@ contexts:
captures:
1: keyword.declaration.namespace.scala
2: entity.name.namespace.header.scala
- # what follows is identical to case-pattern, except it goes to case-body-first
- - match: '\bcase\b(?!\s+class\b)'
- scope: keyword.declaration.other.scala
+ - match: '\bgiven\b'
+ scope: keyword.declaration.given.scala
push:
- - meta_content_scope: meta.pattern.scala
- - match: '(?=\bclass\b)'
+ - match: '\bwith\b'
+ scope: keyword.declaration.scala
+ pop: true
+ - match: '{{id}}(?=\s*{{withinbrackets}}?{{withinparens}}?:)'
+ scope: variable.other.constant.scala
+ - match: '{{id}}'
+ scope: support.class.scala
+ - match: '\['
+ scope: punctuation.definition.generic.begin.scala
+ push: function-tparams-brackets
+ - match: '\('
+ scope: punctuation.section.group.begin.scala
+ push: function-parameter-list-contents
+ - match: '(?=\S)'
+ pop: true
+ - match: '^\s*(end)\b'
+ captures:
+ 1: keyword.control.section.end.scala
+ push:
+ - match: '\bextension\b'
+ scope: keyword.declaration.extension.scala
pop: true
- match: '\bif\b'
scope: keyword.control.flow.scala
+ pop: true
+ - match: '\bfor\b'
+ scope: keyword.control.flow.scala
+ pop: true
+ - match: '\bgiven\b'
+ scope: keyword.declaration.given.scala
+ pop: true
+ - match: '\bmatch\b'
+ scope: keyword.control.flow.scala
+ pop: true
+ - match: '\bnew\b'
+ scope: keyword.other.scala
+ pop: true
+ - match: '\bthis\b'
+ scope: variable.language.scala
+ pop: true
+ - match: '\btry\b'
+ scope: keyword.control.exception.scala
+ pop: true
+ - match: '\bval\b'
+ scope: storage.type.stable.scala
+ pop: true
+ - match: '\bwhile\b'
+ scope: keyword.control.flow.scala
+ pop: true
+ # what should we scope this?
+ - match: '{{id}}'
+ pop: true
+ - match: (?=\S)
+ pop: true
+ - match: '\bextension\b(?=\s*[(\[])'
+ scope: keyword.declaration.extension.scala
+ push:
+ - match: '\['
+ scope: punctuation.definition.generic.begin.scala
+ push: function-tparams-brackets
+ - match: '\('
+ scope: punctuation.section.group.begin.scala
+ push: function-parameter-list-contents
+ - match: '(?=\S)'
+ pop: true
+ - match: '\benum\b'
+ scope: keyword.declaration.enum.scala
+ push:
+ - match: '{{id}}'
+ scope: entity.name.enum.scala
set:
- - match: '{{rightarrow}}'
- scope: keyword.declaration.function.arrow.case.scala
- set: case-body-first
- - include: main-no-lambdas
- # eliminates arrow from the pattern meta scope
- - match: '(?={{rightarrow}})'
- set:
- - match: '{{rightarrow}}'
- scope: keyword.declaration.function.arrow.case.scala
- set: case-body-first
- - match: '(?=\}|\bcase\b)' # makes typing more pleasant
+ - match: '\['
+ scope: punctuation.definition.generic.begin.scala
+ push: function-tparams-brackets
+ - match: '\bderives\b'
+ scope: storage.modifier.derives.scala
+ set:
+ - match: '{{id}}'
+ scope: entity.other.inherited-class.scala
+ - match: ','
+ scope: punctuation.separator.scala
+ - match: '(?=\S)'
+ pop: true
+ - match: '(?=\S)'
+ pop: true
+ - match: '(?=\S)'
pop: true
- - include: pattern-match
- case-body-first:
- - meta_content_scope: meta.block.case.first.scala
- - match: \{
- scope: punctuation.section.block.begin.scala
- set:
- - meta_scope: meta.block.scala
- - match: \}
- scope: punctuation.section.block.end.scala
- set: case-body-first
- - include: main
- - include: case-body
-
- case-body-non-first:
- - meta_content_scope: meta.block.case.non-first.scala
- - match: \{
- scope: punctuation.section.block.begin.scala
+ # this one is meant to be ambiguous with case-pattern
+ # this is a really unfortunate artifact of braceless enums
+ case-decl:
+ # this one happens when we have a bare line with case class
+ # the user is probably in the process of typing an id, which will get caught by an earlier match
+ - match: '\b(case)\s+(class)\b'
+ captures:
+ 1: keyword.declaration.class.scala
+ 2: keyword.declaration.class.scala
+ pop: true
+ - match: '\bcase\b'
+ scope: keyword.declaration.other.scala
set:
- - meta_scope: meta.block.scala
- - match: \}
- scope: punctuation.section.block.end.scala
- set: case-body-non-first
- - include: main
- - include: case-body
+ - match: '(?={{id}}\s*,)'
+ set:
+ # this is intended to help people diagnose a bit
+ - match: '{{rightarrow}}'
+ scope: invalid.case-list.scala
+ - match: '{{id}}'
+ scope: entity.name.enum.scala
+ - match: '(,)\s*$'
+ captures:
+ 1: punctuation.separator.scala
+ - match: ','
+ scope: punctuation.separator.scala
+ - match: '$'
+ pop: true
+ - match: '(?=\S)'
+ pop: true
+ - match: '{{keywords}}'
+ fail: case
+ - match: '{{id}}'
+ scope: entity.name.enum.scala
+ set:
+ - match: '{{rightarrow}}'
+ fail: case
+ - match: '$'
+ pop: true
+ - match: '(?=\S)'
+ set: class-type-parameter-list-maybe-case
+ - match: '\S'
+ fail: case
- # this exists to clear the meta_scope from first or non-first
case-pattern:
- match: '\bcase\b'
scope: keyword.declaration.other.scala
@@ -475,25 +603,18 @@ contexts:
set:
- match: '{{rightarrow}}'
scope: keyword.declaration.function.arrow.case.scala
- set: case-body-non-first
+ pop: true
- include: main-no-lambdas
# eliminates arrow from the pattern meta scope
- match: '(?={{rightarrow}})'
set:
- match: '{{rightarrow}}'
scope: keyword.declaration.function.arrow.case.scala
- set: case-body-non-first
+ pop: true
- match: '(?=\}|\bcase\b)' # makes typing more pleasant
pop: true
- include: pattern-match
- case-body:
- - match: (?=\bcase\b)
- set: case-pattern
- - match: (?=\})
- pop: true
- - include: main
-
braces:
- match: \[
scope: punctuation.definition.generic.begin.scala
@@ -578,30 +699,7 @@ contexts:
function-parameter-list:
- match: '\('
scope: punctuation.section.group.begin.scala
- push:
- - match: '\)'
- scope: punctuation.section.group.end.scala
- pop: true
- - match: '({{alphaid}})(?=\s*:)'
- captures:
- 1: variable.parameter.scala
- push:
- - match: '{{typeprefix}}'
- captures:
- 1: punctuation.ascription.scala
- set:
- - match: '(\*)\s*(?=[\),=])'
- captures:
- 1: keyword.operator.varargs.scala
- - match: '(?=\))'
- pop: true
- - match: ','
- scope: punctuation.separator.scala
- pop: true
- - include: delimited-type-expression
- - match: '(?=[=])'
- pop: true
- - include: main
+ push: function-parameter-list-contents
- match: ':'
scope: punctuation.ascription.scala
set: function-return-type-definition
@@ -610,6 +708,44 @@ contexts:
- match: '(?=\S)'
pop: true
+ function-parameter-list-contents:
+ - meta_scope: meta.group.scala
+ - match: '\)'
+ scope: punctuation.section.group.end.scala
+ pop: true
+ - match: '\b(?:using|implicit)\b'
+ scope: storage.modifier.other.scala
+ - match: '({{alphaid}})(?=\s*:)'
+ captures:
+ 1: variable.parameter.scala
+ push:
+ - match: '{{typeprefix}}'
+ captures:
+ 1: punctuation.ascription.scala
+ set:
+ - match: '(\*)\s*(?=[\),=])'
+ captures:
+ 1: keyword.operator.varargs.scala
+ - match: '(?=\))'
+ pop: true
+ - match: ','
+ scope: punctuation.separator.scala
+ pop: true
+ - include: delimited-type-expression
+ - match: '(?=[=])'
+ pop: true
+ - match: '(?={{id}})'
+ push:
+ - match: '(?=[,)])'
+ pop: true
+ - include: delimited-type-expression
+ - match: '='
+ scope: keyword.operator.assignment.scala
+ push:
+ - match: '(?=[,)])'
+ pop: true
+ - include: main
+
function-parameter-list-newline:
- include: decl-newline-double-check
- match: '(?=\S)'
@@ -628,13 +764,15 @@ contexts:
- match: '\['
scope: punctuation.definition.generic.begin.scala
push: class-tparams-brackets
+ - match: '(?=\bderives\b)'
+ set: enum-inheritance-derives
- match: '(?=\b(?:extends|with)\b)'
set: class-inheritance-extends
- match: '(?=\{)'
set: class-pre-inheritance-early-initializer
- match: '\n'
set: class-type-parameter-list-newline
- - match: (?=\S)
+ - match: '(?=\S)'
pop: true
class-type-parameter-list-newline:
@@ -642,6 +780,41 @@ contexts:
- match: '(?=\S)'
set: class-type-parameter-list
+ # this is analogous to its namesake, but we may or may not be in a pattern match
+ # anything which *can't* occur in a pattern should collapse us to the namesake state
+ class-type-parameter-list-maybe-case:
+ - match: '{{rightarrow}}'
+ fail: case
+ - match: '(?=\b(?:private|protected)\b)'
+ set: class-type-parameter-list
+ - match: (?=@{{plainid}})
+ set:
+ - include: annotation
+ - match: '(?=\S)'
+ set: class-type-parameter-list-maybe-case
+ - match: '(?=\()'
+ set: class-parameter-list-maybe-case
+ - match: '\['
+ scope: punctuation.definition.generic.begin.scala
+ push: class-tparams-brackets
+ - match: '(?=\bderives\b)'
+ set: enum-inheritance-derives
+ - match: '(?=\b(?:extends|with)\b)'
+ set: class-inheritance-extends
+ - match: '(?=\{)'
+ set: class-pre-inheritance-early-initializer
+ - match: '\n'
+ set: class-type-parameter-list-newline-maybe-case
+ - match: '(?=\S)'
+ fail: case
+
+ class-type-parameter-list-newline-maybe-case:
+ - match: '{{rightarrow}}'
+ fail: case
+ - include: decl-newline-double-check
+ - match: '(?=\S)'
+ set: class-type-parameter-list-maybe-case
+
class-tparams-brackets:
- meta_scope: meta.generic.scala
- match: '\['
@@ -686,6 +859,8 @@ contexts:
- match: '(?=[=])'
pop: true
- include: main
+ - match: '(?=\bderives\b)'
+ set: enum-inheritance-derives
- match: '(?=\b(?:extends|with)\b)'
set: class-inheritance-extends
- match: '(?=\{)'
@@ -700,7 +875,62 @@ contexts:
- match: '(?=\S)'
set: class-parameter-list
+ class-parameter-list-maybe-case:
+ - match: '{{rightarrow}}'
+ fail: case
+ - match: '\('
+ scope: punctuation.section.group.begin.scala
+ push:
+ - meta_scope: meta.group.scala
+ - match: '\)'
+ scope: punctuation.section.group.end.scala
+ pop: true
+ - match: '\bval\b'
+ scope: storage.type.scala
+ - match: '\bvar\b'
+ scope: storage.type.volatile.scala
+ - match: '({{alphaid}})(?=\s*:)'
+ captures:
+ 1: variable.parameter.scala
+ push:
+ - match: '{{typeprefix}}'
+ captures:
+ 1: punctuation.ascription.scala
+ set:
+ - match: '(\*)\s*(?=[\),=])'
+ captures:
+ 1: keyword.operator.varargs.scala
+ - match: '(?=\))'
+ pop: true
+ - match: ','
+ scope: punctuation.separator.scala
+ pop: true
+ - include: delimited-type-expression
+ - match: '(?=[=])'
+ pop: true
+ - include: main
+ - match: '(?=\bderives\b)'
+ set: enum-inheritance-derives
+ - match: '(?=\b(?:extends|with)\b)'
+ set: class-inheritance-extends
+ - match: '(?=\{)'
+ set: class-pre-inheritance-early-initializer
+ - match: '\n'
+ set: class-parameter-list-newline-maybe-case
+ - match: '(?=\S)'
+ fail: case
+
+ class-parameter-list-newline-maybe-case:
+ - match: '{{rightarrow}}'
+ fail: case
+ - include: decl-newline-double-check
+ - match: '(?=\S)'
+ set: class-parameter-list-maybe-case
+
class-pre-inheritance-early-initializer:
+ # class declaration is over, we're doing some sort of self type
+ - match: '(?=\{\s*{{lambdalookaheadtyped}})'
+ pop: true
- match: \{
scope: punctuation.section.braces.begin.scala
push:
@@ -709,6 +939,8 @@ contexts:
scope: punctuation.section.braces.end.scala
pop: true
- include: main
+ - match: '(?=\bderives\b)'
+ set: enum-inheritance-derives
- match: '(?=\b(?:extends|with)\b)'
set: class-inheritance-extends
- match: '(?=\S)'
@@ -761,7 +993,7 @@ contexts:
- include: delimited-type-expression
- match: '\n'
set: class-inheritance-extends-token-newline
- - match: (?=\S)
+ - match: '(?=\S)'
pop: true
class-inheritance-extends-token-newline:
@@ -785,13 +1017,18 @@ contexts:
scope: punctuation.definition.generic.end.scala
pop: true
- include: delimited-type-expression
- - match: (?=\b(?:with|extends)\b)
+ - match: ','
+ scope: punctuation.separator.scala
+ set: class-inheritance-extends-token
+ - match: '(?=\bderives\b)'
+ set: enum-inheritance-derives
+ - match: '(?=\b(?:with|extends)\b)'
set: class-inheritance-with
- - match: (?=\{)
+ - match: '(?=\{)'
set: class-inheritance-early-initializer
- match: '\n'
set: class-inheritance-extends-token-after-newline
- - match: (?=\S)
+ - match: '(?=\S)'
pop: true
class-inheritance-extends-token-after-newline:
@@ -808,6 +1045,8 @@ contexts:
scope: punctuation.section.braces.end.scala
pop: true
- include: main
+ - match: '(?=\bderives\b)'
+ set: enum-inheritance-derives
- match: '(?=\bwith\b)'
set: class-inheritance-with
- match: '(?=\S)'
@@ -837,8 +1076,18 @@ contexts:
- match: '(?=\S)'
set: class-inheritance-with
+ enum-inheritance-derives:
+ - match: '\bderives\b'
+ scope: storage.modifier.derives.scala
+ - match: '{{id}}'
+ scope: entity.other.inherited-class.scala
+ - match: ','
+ scope: punctuation.separator.scala
+ - match: '(?=\S)'
+ pop: true
+
imports:
- - match: \bimport\b
+ - match: '\b(?:import|export)\b'
scope: keyword.declaration.import.scala
push:
- meta_scope: meta.import.scala
@@ -849,6 +1098,20 @@ contexts:
scope: punctuation.separator.scala
- match: '(?=[\n;])'
pop: true
+ - match: '\bgiven\b'
+ scope: storage.modifier.other.scala # this is pretty weird tbh
+ set:
+ - match: '(?=[\n;])'
+ pop: true
+ - match: '(?={{id}})'
+ set:
+ - match: '(?=[\n;])'
+ pop: true
+ - include: delimited-type-expression
+ - match: '(?=\S)'
+ pop: true
+ - match: '\*'
+ scope: variable.language.wildcard.scala
- match: '{{id}}'
- match: \.
scope: punctuation.accessor.dot.scala
@@ -865,6 +1128,20 @@ contexts:
scope: punctuation.separator.scala
- match: '{{rightarrow}}'
scope: keyword.operator.arrow.scala
+ - match: '\bas\b'
+ scope: keyword.operator.as.scala
+ - match: '\bgiven\b'
+ scope: storage.modifier.other.scala
+ push:
+ - match: '(?={{id}})'
+ set:
+ - match: '(?=[,\n;}])'
+ pop: true
+ - include: delimited-type-expression
+ - match: '(?=\S)'
+ pop: true
+ - match: '\*'
+ scope: variable.language.wildcard.scala
- match: '{{id}}'
- match: '_'
scope: variable.language.underscore.scala
@@ -969,29 +1246,50 @@ contexts:
- match: '(?=[^ \t])'
pop: true
- for-comprehension:
- - match: '\b(for)\s*(\{)'
- captures:
- 1: keyword.control.flow.scala
- 2: punctuation.section.block.begin.scala
+ if-statement:
+ - match: '\bif\b'
+ scope: keyword.control.flow.scala
push:
- - match: '\}'
- scope: punctuation.section.block.end.scala
+ - match: '$'
pop: true
- - include: for-braces-body
- - match: '\b(for)\s*(\()'
- captures:
- 1: keyword.control.flow.scala
- 2: punctuation.section.group.begin.scala
+ - match: '\bthen\b'
+ scope: keyword.control.flow.scala
+ - include: main
+
+ for-comprehension:
+ - match: '\bfor\b'
+ scope: keyword.control.flow.scala
push:
- - match: '\)'
- scope: punctuation.section.group.end.scala
+ - match: '\byield\b'
+ scope: keyword.control.flow.scala
+ pop: true
+ - match: '\('
+ scope: punctuation.section.group.begin.scala
+ set:
+ - match: '\)'
+ scope: punctuation.section.group.end.scala
+ pop: true
+ - include: for-parens-body
+ - match: '\{'
+ scope: punctuation.section.block.begin.scala
+ set:
+ - match: '\}'
+ scope: punctuation.section.block.end.scala
+ pop: true
+ - include: for-braces-body
+ - match: '(?=[)};])'
pop: true
- - include: for-parens-body
+ - match: '(?=\S)'
+ set:
+ - match: '\b(?:yield|do)\b'
+ scope: keyword.control.flow.scala
+ pop: true
+ - include: for-braces-body
+
for-braces-body:
- match: |-
(?x)
- ^(?=
+ (?=
(
[^<\x{2190}=\{\}/"]
|<[^\-]
@@ -1024,6 +1322,7 @@ contexts:
- match: '(?=\))'
pop: true
- match: ;
+ scope: punctuation.terminator.scala
pop: true
- include: main
@@ -1090,11 +1389,15 @@ contexts:
storage-modifiers:
- match: '\b(?:private\[\S+\]|protected\[\S+\]|private|protected)\b'
scope: storage.modifier.access.scala
- - match: \b(?:abstract|final|lazy|sealed|implicit|override)\b
+ # we treat inline as a hard modifier because it's too convoluted to treat it softly
+ - match: '\b(?:abstract|final|lazy|sealed|implicit|override|using|inline)\b'
+ scope: storage.modifier.other.scala
+ # soft modifiers
+ - match: '\b(?:infix|opaque|open|transparent)\b(?=\s+(?:private|protected|abstract|final|lazy|sealed|implicit|override|def|val|var|type|given|class|trait|object|enum|case\s+class|case\s+object|inline)\b)'
scope: storage.modifier.other.scala
# see http://www.scala-lang.org/docu/files/ScalaReference.pdf part 1.3.5-6 (page 18)
- strings:
+ base-strings:
- match: '"""'
scope: punctuation.definition.string.begin.scala
push:
@@ -1116,6 +1419,9 @@ contexts:
- match: \n
scope: invalid.string.newline.scala
- include: escaped
+
+ strings:
+ - include: base-strings
- match: '(f)(""")'
captures:
1: support.function.scala
@@ -1509,6 +1815,12 @@ contexts:
- match: '[`\n]'
scope: punctuation.definition.identifier.scala
pop: true
+ - match: '\bgiven\b'
+ scope: keyword.declaration.given.scala
+ push:
+ - match: '(?=[,)]|{{rightarrow}}|<-|\x{2190}|=|\bdo\b|\byield\b)'
+ pop: true
+ - include: delimited-type-expression
- match: '{{varid}}(?:(\.){{varid}})+'
captures:
1: punctuation.accessor.scala
@@ -1556,7 +1868,8 @@ contexts:
captures:
1: punctuation.ascription.scala
push:
- - match: '(?=[,\)@|])'
+ # leading whitespace on | is required due to the weird matching in type expressions
+ - match: '(?=[,\)@]|\s*\|)'
pop: true
- include: delimited-type-expression
- match: \(
@@ -1568,7 +1881,7 @@ contexts:
- include: nested-pattern-match
pattern-match:
- include: base-pattern-match
- - include: ascription-no-function
+ - include: ascription-no-function-no-or
- match: \(
scope: punctuation.section.group.begin.scala
push:
@@ -1577,7 +1890,10 @@ contexts:
pop: true
- include: nested-pattern-match
- base-type-expression-no-function:
+ base-type-expression-no-function-no-or:
+ - include: literal-constants-base
+ - include: char-literal
+ - include: base-strings
- match: ;
scope: punctuation.terminator.scala
pop: true
@@ -1587,7 +1903,7 @@ contexts:
- match: \)
scope: punctuation.definition.group.end.scala
pop: true
- - include: delimited-type-expression
+ - include: delimited-type-group-expression
- match: \[
scope: punctuation.definition.generic.begin.scala
push:
@@ -1604,6 +1920,22 @@ contexts:
- include: declarations
- match: '_\s*\*'
scope: keyword.operator.varargs.scala
+ # the whitespace covering is required to deal with the very aggressive scope popping for single type exprs
+ - match: '\s*(=>>)\s*'
+ captures:
+ 1: keyword.operator.arrow.type-lambda.scala
+ - match: '\s*(\?=>)\s*'
+ captures:
+ 1: keyword.operator.arrow.type-context.scala
+ - match: '\s*(&)\s*'
+ captures:
+ 1: keyword.operator.and.scala
+
+ base-type-expression-no-function:
+ - include: base-type-expression-no-function-no-or
+ - match: '\s*(\|)\s*'
+ captures:
+ 1: keyword.operator.or.scala
base-type-expression:
- include: base-type-expression-no-function
@@ -1615,6 +1947,7 @@ contexts:
scope: keyword.operator.bound.scala
delimited-type-expression:
+ - include: base-type-expression
- include: annotation
# kind-projector support
- match: '([+-])?([?*])'
@@ -1632,10 +1965,6 @@ contexts:
scope: punctuation.accessor.scala
- match: ','
scope: punctuation.separator.scala
- # - include: literal-constants
- # - include: char-literal
- # - include: scala-symbol
- # - include: strings
- include: base-types
- match: '\b(?:forSome)\b'
scope: keyword.declaration.scala
@@ -1651,9 +1980,38 @@ contexts:
scope: support.type.scala
- match: '(<:|>:)'
scope: keyword.operator.bound.scala
- - include: base-type-expression
# single-type is a type expression with semicolon inference
+ single-type-expression-no-function-no-or:
+ - match: '\btype\b'
+ scope: keyword.other.scala
+ - match: \b(?:Unit|Boolean|Byte|Char|Short|Int|Float|Long|Double)\b
+ scope: storage.type.primitive.scala
+ set: single-type-expression-tail-no-function-no-or
+ - match: '\b(?:forSome)\b'
+ scope: keyword.declaration.scala
+ - match: '\b(?:this|super)\b'
+ scope: variable.language.scala
+ set: single-type-expression-tail-no-function-no-or
+ - match: '{{upperid}}'
+ scope: support.class.scala
+ set: single-type-expression-tail-no-function-no-or
+ - match: '{{typeid}}'
+ scope: support.type.scala
+ set: single-type-expression-tail-no-function-no-or
+ - match: (?=@{{plainid}})
+ set: single-type-expression-tail-no-function-no-or
+ - match: \(
+ scope: punctuation.definition.generic.begin.scala
+ set:
+ - match: \)
+ scope: punctuation.definition.generic.end.scala
+ set: single-type-expression-tail-no-function-no-or
+ - include: delimited-type-expression
+ - include: base-type-expression-no-function-no-or
+ - match: '(?=.)' # we can be SUPER aggressive about popping out here
+ pop: true
+
single-type-expression-no-function:
- match: '\btype\b'
scope: keyword.other.scala
@@ -1668,10 +2026,6 @@ contexts:
- match: '{{upperid}}'
scope: support.class.scala
set: single-type-expression-tail-no-function
- # - include: literal-constants
- # - include: char-literal
- # - include: scala-symbol
- # - include: strings
- match: '{{typeid}}'
scope: support.type.scala
set: single-type-expression-tail-no-function
@@ -1707,10 +2061,6 @@ contexts:
- match: '{{upperid}}'
scope: support.class.scala
set: single-type-expression-tail
- # - include: literal-constants
- # - include: char-literal
- # - include: scala-symbol
- # - include: strings
- match: '(?={{keywords}})'
pop: true
- match: '{{typeid}}'
@@ -1718,17 +2068,33 @@ contexts:
set: single-type-expression-tail
- match: (?=@{{plainid}})
set: single-type-expression-tail
- - match: \(
+ - match: '\['
+ scope: punctuation.definition.generic.begin.scala
+ set:
+ - meta_scope: meta.generic.scala
+ - match: '\]'
+ scope: punctuation.definition.generic.end.scala
+ set: single-type-expression-tail
+ - include: delimited-type-expression
+ - match: '\('
scope: punctuation.definition.group.begin.scala
set:
- - match: \)
+ - match: '\)'
scope: punctuation.definition.group.end.scala
set: single-type-expression-tail
- - include: delimited-type-expression
+ - include: delimited-type-group-expression
- include: base-type-expression
- match: '(?=.)' # we can be SUPER aggressive about popping out here
pop: true
+ delimited-type-group-expression:
+ - match: '({{id}})(?=\s*:)'
+ captures:
+ 1: variable.parameter.scala
+ - match: ':'
+ scope: punctuation.ascription.scala
+ - include: delimited-type-expression
+
# single-type-expression, but with an allowance for one *leading* newline and whitespace
single-type-expression-leading-newline:
- match: \n
@@ -1777,10 +2143,6 @@ contexts:
- match: '{{upperid}}'
scope: support.class.scala
set: single-type-expression-tail-no-function-type-expectation
- # - include: literal-constants
- # - include: char-literal
- # - include: scala-symbol
- # - include: strings
- match: '(?={{keywords}})'
pop: true
- match: '{{typeid}}'
@@ -1801,6 +2163,64 @@ contexts:
- match: '(?=\S)'
pop: true
+ single-type-expression-tail-no-function-no-or:
+ - match: (?=@{{plainid}})
+ set:
+ - include: annotation
+ - match: '(?=\S)'
+ set: single-type-expression-tail-no-function-no-or
+ - match: '[\.#]'
+ scope: punctuation.accessor.scala
+ set: single-type-expression-no-function-no-or
+ - match: \[
+ scope: punctuation.definition.generic.begin.scala
+ set:
+ - meta_scope: meta.generic.scala
+ - match: \]
+ scope: punctuation.definition.generic.end.scala
+ set: single-type-expression-tail-no-function-no-or
+ - include: delimited-type-expression
+ - match: '\b(with)(?:\s+|\b)'
+ captures:
+ 1: keyword.declaration.scala
+ set: single-type-expression-no-function-no-or
+ - match: '\btype\b'
+ scope: invalid.keyword.type.in-tail-position.scala
+ - match: \b(?:Unit|Boolean|Byte|Char|Short|Int|Float|Long|Double)\b
+ scope: storage.type.primitive.scala
+ set: single-type-expression-tail-no-function-no-or-type-expectation
+ - match: '\b(?:forSome)\b'
+ scope: keyword.declaration.scala
+ - match: '\b(?:this|super)\b'
+ scope: variable.language.scala
+ set: single-type-expression-tail-no-function-no-or-type-expectation
+ - match: '{{upperid}}'
+ scope: support.class.scala
+ set: single-type-expression-tail-no-function-no-or-type-expectation
+ - match: '(?={{keywords}})'
+ pop: true
+ # we need to explicitly match this one because "|" is a valid type name
+ - match: '(?=\|)'
+ pop: true
+ - match: '{{typeid}}'
+ scope: support.type.scala
+ set: single-type-expression-tail-no-function-no-or-type-expectation
+ - match: '\{'
+ scope: punctuation.definition.block.begin.scala
+ set:
+ - meta_scope: meta.block.scala
+ - match: \}
+ scope: punctuation.definition.block.end.scala
+ set: single-type-expression-tail-no-function-no-or
+ - include: main
+ - match: '(?=\()'
+ push: try-dispatch
+ - include: base-type-expression-no-function-no-or
+ - match: \n
+ pop: true
+ - match: '(?=\S)'
+ pop: true
+
single-type-expression-tail:
- match: (?=@{{alphaplainid}})
set:
@@ -1836,13 +2256,17 @@ contexts:
- match: '\b(?:this|super)\b'
scope: variable.language.scala
set: single-type-expression-tail-type-expectation
+ - match: '\bmatch\b'
+ scope: keyword.control.flow.scala
+ push:
+ - match: '\n'
+ set: type-match-body
+ - match: '(?=\{)'
+ set: type-match-body
+ - include: type-match-body
- match: '{{upperid}}'
scope: support.class.scala
set: single-type-expression-tail-type-expectation
- # - include: literal-constants
- # - include: char-literal
- # - include: scala-symbol
- # - include: strings
- match: '(?={{keywords}})'
pop: true
- match: '{{typeid}}'
@@ -1866,6 +2290,61 @@ contexts:
- match: '(?=\S)'
pop: true
+ type-match-body:
+ - match: '\{'
+ scope: punctuation.definition.block.begin.scala
+ set: delimited-type-match-body
+ - match: '\n'
+ set: type-match-body-newline
+ - match: '\bcase\b'
+ scope: keyword.declaration.other.scala
+ set:
+ - match: '{{rightarrow}}'
+ scope: keyword.declaration.function.arrow.case.scala
+ set: type-case-rightarrow
+ - include: delimited-type-expression
+ - match: '(?=\S)'
+ pop: true
+
+ type-match-body-newline:
+ - include: decl-newline-double-check
+ - match: '(?=\S)'
+ set: type-match-body
+
+ delimited-type-match-body:
+ - match: '\}'
+ scope: punctuation.definition.block.end.scala
+ pop: true
+ - match: '\bcase\b'
+ scope: keyword.declaration.other.scala
+ set:
+ - match: '{{rightarrow}}'
+ scope: keyword.declaration.function.arrow.case.scala
+ set: delimited-type-case-rightarrow
+ - include: delimited-type-expression
+
+ type-case-rightarrow:
+ - match: '\n'
+ set: type-case-rightarrow-newline
+ - match: '(?=\bcase\b)'
+ set: type-match-body
+ - include: delimited-type-expression
+
+ type-case-rightarrow-newline:
+ - match: '(?=\bcase\b)'
+ set: type-match-body
+ - include: decl-newline-double-check
+ - match: '(?=\S)'
+ set: type-case-rightarrow
+
+ delimited-type-case-rightarrow:
+ - match: '\}'
+ scope: punctuation.definition.block.end.scala
+ pop: true
+ - match: '(?=\bcase\b)'
+ set: delimited-type-match-body
+ - include: delimited-type-expression
+
# we saw an infix type component, eat a single newline
single-type-expression-tail-no-function-type-expectation:
- match: \n
@@ -1873,6 +2352,12 @@ contexts:
- match: '(?=\S)'
set: single-type-expression-no-function
+ single-type-expression-tail-no-function-no-or-type-expectation:
+ - match: \n
+ set: single-type-expression-tail-no-function-no-or-newline
+ - match: '(?=\S)'
+ set: single-type-expression-no-function-no-or
+
single-type-expression-tail-type-expectation:
- match: \n
set: single-type-expression-tail-newline
@@ -1884,6 +2369,11 @@ contexts:
- match: '(?=\S)'
set: single-type-expression-no-function
+ single-type-expression-tail-no-function-no-or-newline:
+ - include: decl-newline-double-check
+ - match: '(?=\S)'
+ set: single-type-expression-no-function-no-or
+
single-type-expression-tail-newline:
- include: decl-newline-double-check
- match: '(?=\S)'
diff --git a/Scala/syntax_test_scala.scala b/Scala/syntax_test_scala.scala
index cc9def461f..67a0bcd05c 100644
--- a/Scala/syntax_test_scala.scala
+++ b/Scala/syntax_test_scala.scala
@@ -923,16 +923,20 @@ type Foo >: Bar
{ a => ??? }
// ^ variable.parameter
+// ^^^^^^^^^^^^ meta.block.scala
{ (a, b) => ??? }
-// ^ punctuation.section.group.begin.scala
+// ^ punctuation.section.block.begin.scala
// ^ variable.parameter.scala
// ^ variable.parameter.scala
// ^ punctuation.section.group.end.scala
+// ^ punctuation.section.block.end.scala
{ a: Int => ??? }
+// ^ punctuation.section.block.begin.scala
// ^ variable.parameter
// ^^^ storage.type.primitive.scala
+// ^ punctuation.section.block.end.scala
{ (a: Int, b: Int) => ??? }
// ^ variable.parameter
@@ -967,9 +971,9 @@ type Foo >: Bar
a => ???
// ^ variable.parameter
- a: Int => ???
-// ^ variable.parameter
-// ^^^ storage.type.primitive.scala
+ (a: Int) => ???
+// ^ variable.parameter
+// ^^^ storage.type.primitive.scala
{
case _ if thing =>
@@ -1031,10 +1035,10 @@ foo(())()
// ^^^^^^^^^^^^ string.quoted.double
// ^^^^^^^^^^^^ - comment
- cb: ((Throwable \/ Unit) => Unit) => 42
-// ^^ variable.parameter
-// ^^ support.type.scala
-// ^^ keyword.declaration.function.arrow
+ (cb: ((Throwable \/ Unit) => Unit)) => 42
+// ^^ variable.parameter
+// ^^ support.type.scala
+// ^^ keyword.declaration.function.arrow
def foo(a: A <:< B)
// ^^^ support.type.scala
@@ -1256,9 +1260,9 @@ val Stuff(thing, other) = ???
// ^^^^^ variable.other.constant.scala
// ^^^^^ variable.other.constant.scala
- x: List[Int] => ()
-// ^ variable.parameter.scala
-// ^^ keyword.declaration.function.arrow.lambda.scala
+ (x: List[Int]) => ()
+// ^ variable.parameter.scala
+// ^^ keyword.declaration.function.arrow.lambda.scala
/** private */ class Foo
// ^^^^^ keyword.declaration.class.scala
@@ -1409,13 +1413,13 @@ def test
def foo: Map[Bar]=42
// ^^ meta.number.integer.decimal.scala
- x: Foo.Bar => ()
-// ^ variable.parameter.scala
-// ^^ keyword.declaration.function.arrow.lambda.scala
+ (x: Foo.Bar) => ()
+// ^ variable.parameter.scala
+// ^^ keyword.declaration.function.arrow.lambda.scala
- x: Foo#Bar => ()
-// ^ variable.parameter.scala
-// ^^ keyword.declaration.function.arrow.lambda.scala
+ (x: Foo#Bar) => ()
+// ^ variable.parameter.scala
+// ^^ keyword.declaration.function.arrow.lambda.scala
object Stuff {
case
@@ -1640,22 +1644,11 @@ match {
//^^^^ - meta.pattern
// ^ meta.pattern.scala
=> 42
-// ^^ - meta.block.case.first
// ^^ - meta.pattern
-// ^^ meta.block.case.first.scala
-{
-
- // <- - meta.block.case
-}
case _ => 42
-// ^^ - meta.block.case.non-first
// ^^ - meta.pattern
-// ^^ meta.block.case.non-first.scala
- case _ => 42
-// ^^ - meta.block.case.non-first
-// ^^ meta.block.case.non-first.scala
}
class Foo
@@ -2024,8 +2017,10 @@ for (_<- fu; _← fu; _= fu)
// ^ punctuation.section.group.begin.scala
// ^ variable.language.underscore.scala
// ^^ keyword.operator.assignment.scala
+// ^ punctuation.terminator.scala
// ^ variable.language.underscore.scala
// ^ keyword.operator.assignment.scala
+// ^ punctuation.terminator.scala
// ^ variable.language.underscore.scala
// ^ keyword.operator.assignment.scala
// ^ punctuation.section.group.end.scala
@@ -2303,3 +2298,29 @@ completed: F[_ >: A] => B)
// ^ entity.name.type.scala
}
+(abc, cba) => ()
+// ^ punctuation.separator.scala
+
+class c()
+ extends a()
+ // some comment
+ with foo with bar {
+// ^^^^ storage.modifier.with.scala
+// ^^^ entity.other.inherited-class.scala
+// ^^^^ storage.modifier.with.scala
+// ^^^ entity.other.inherited-class.scala
+
+trait Foo { abcd: Foo =>
+// ^^^^ variable.parameter.scala
+// ^^^ support.class.scala
+// ^^ keyword.declaration.function.arrow.lambda.scala
+}
+
+trait Foo { abcd: Foo.type =>
+// ^^^^ variable.parameter.scala
+// ^^^ support.class.scala
+// ^^^^ keyword.other.scala
+// ^^ keyword.declaration.function.arrow.lambda.scala
+}
+
+type Foo = Foo.type
diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala
new file mode 100644
index 0000000000..d1ba964c0d
--- /dev/null
+++ b/Scala/syntax_test_scala3.scala
@@ -0,0 +1,599 @@
+// SYNTAX TEST "Packages/Scala/Scala.sublime-syntax"
+// this test is designed to check the Scala 3 specific features
+
+val then = 42
+// ^^^^ variable.other.constant.scala
+
+if x < 0 then
+// ^^^^ keyword.control.flow.scala
+ "negative"
+else if x == 0 then
+// ^^^^ keyword.control.flow.scala
+ "zero"
+else
+ "positive"
+
+if x < 0 then -x else x
+// ^^^^ keyword.control.flow.scala
+
+while x >= 0 do x = f(x)
+// ^^ keyword.control.flow.scala
+
+for x <- xs if x > 0
+// ^ variable.parameter.scala
+// ^^ keyword.operator.assignment.scala
+yield x * x
+
+for
+ x <- xs
+//^ variable.parameter.scala
+// ^^ keyword.operator.assignment.scala
+ y <- ys
+//^ variable.parameter.scala
+// ^^ keyword.operator.assignment.scala
+do
+ println(x + y)
+
+try body
+catch case ex: IOException => handle;
+
+fooBar:
+ // ^ punctuation.section.begin.scala
+ test
+ test
+
+ohboy: x => 42
+// ^^ - variable
+// ^ punctuation.section.begin.scala
+// ^ variable.parameter.scala
+// ^^ keyword.declaration.function.arrow.lambda.scala
+
+againmore: (x, y) => 42
+// ^ variable.parameter.scala
+// ^ variable.parameter.scala
+
+againmore: (x, y)
+// ^ punctuation.definition.group.begin.scala
+// ^ support.type.scala
+// ^ punctuation.separator.scala
+// ^ support.type.scala
+// ^ punctuation.definition.group.end.scala
+
+confusion: (x: Int) => 12
+// ^ variable.parameter.scala
+// ^ punctuation.ascription.scala
+// ^^^ storage.type.primitive.scala
+
+foo(x: Int, y => 42)
+// ^ punctuation.ascription.scala
+// ^ punctuation.separator.scala
+// ^ variable.parameter.scala.
+// ^^ constant.numeric.value.scala
+
+def foo =
+ ()
+end foo
+// <- keyword.control.section.end.scala
+// ^^^ - support - keyword
+
+object C:
+// ^ punctuation.section.begin.scala
+end C
+// <- keyword.control.section.end.scala
+// ^ - support - keyword
+
+
+end this
+// ^^^^ variable.language.scala
+
+end extension
+// ^^^^^^^^^ keyword.declaration.extension.scala
+
+end given
+// ^^^^^ keyword.declaration.given.scala
+
+end new
+// ^^^ keyword.other.scala
+
+end try
+// ^^^ keyword.control.exception.scala
+
+end if
+// ^^ keyword.control.flow.scala
+
+end match
+// ^^^^^ keyword.control.flow.scala
+
+end val
+// ^^^ storage.type.stable.scala
+
+end while
+// ^^^^^ keyword.control.flow.scala
+
+end for
+// ^^^ keyword.control.flow.scala
+
+List[?]
+// ^ variable.language.hole.scala
+Map[? <: AnyRef, ? >: Null]
+// ^ variable.language.hole.scala
+// ^ variable.language.hole.scala
+
+given intOrd: Ord[Int] with
+// <- keyword.declaration.given.scala
+// ^^^^^^ variable.other.constant.scala
+// ^ punctuation.ascription.scala
+// ^^^ support.class.scala
+// ^^^ storage.type.primitive.scala
+// ^^^^ keyword.declaration.scala
+
+given listOrd[T](using ord: Ord[T]): Ord[List[T]] with
+// <- keyword.declaration.given.scala
+// ^^^^^^^ variable.other.constant.scala
+// ^ support.class.scala
+// ^^^^^ storage.modifier.other.scala
+// ^^^ variable.parameter.scala
+// ^^^ support.class.scala
+
+given Ord[Int] with
+// <- keyword.declaration.given.scala
+// ^^^ support.class.scala
+// ^^^ storage.type.primitive.scala
+// ^^^^ keyword.declaration.scala
+
+given [T](using Ord[T]): Ord[List[T]] with
+// <- keyword.declaration.given.scala
+// ^ support.class.scala
+// ^^^^^ storage.modifier.other.scala
+// ^^^ support.class.scala
+// ^^^^ keyword.declaration.scala
+
+given global: ExecutionContext = ForkJoinPool()
+// <- keyword.declaration.given.scala
+// ^^^^^^ variable.other.constant.scala
+// ^^^^^^^^^^^^^^^^ support.class.scala
+// ^ keyword.operator.assignment.scala
+
+given Position = enclosingTree.position
+// <- keyword.declaration.given.scala
+// ^^^^^^^^ support.class.scala
+// ^ keyword.operator.assignment.scala
+
+given (using config: Config): Factory = MemoizingFactory(config)
+// <- keyword.declaration.given.scala
+// ^^^^^ storage.modifier.other.scala
+// ^^^^^^ variable.parameter.scala
+// ^^^^^^ support.class.scala
+
+for given Context <- applicationContexts do
+// ^^^^^ keyword.declaration.given.scala
+// ^^^^^^^ support.class.scala
+// ^^ keyword.operator.assignment
+
+pair match
+ case (ctx @ given Context, y) => ()
+// ^^^^^ keyword.declaration.given.scala
+// ^^^^^^^ support.class.scala
+
+def max[T](x: T, y: T)(using ord: Ord[T]): T
+// ^^^^^ storage.modifier.other.scala
+// ^^^ variable.parameter.scala
+// ^^^ support.class.scala
+
+max(2, 3)(using intOrd)
+// ^^^^^ storage.modifier.other.scala
+
+def maximum[T](xs: List[T])(using Ord[T]): T
+// ^^^^^ storage.modifier.other.scala
+// ^^^ support.class.scala
+
+class GivenIntBox(using val usingParameter: Int)
+// ^^^^^ storage.modifier.other.scala
+// ^^^ storage.type.scala
+// ^^^^^^^^^^^^^^ variable.parameter.scala
+
+class GivenIntBox2(using usingParameter: Int)
+// ^^^^^ storage.modifier.other.scala
+// ^^^^^^^^^^^^^^ variable.parameter.scala
+
+import b.given
+// ^^^^^ storage.modifier.other.scala
+
+def f(u: Universe)(using ctx: u.Context)(using s: ctx.Symbol, k: ctx.Kind)
+// ^^^^^ storage.modifier.other.scala
+// ^ variable.parameter.scala
+// ^^^ support.type.scala
+// ^ variable.parameter.scala
+// ^^^ support.type.scala
+
+given ctx : global.Context with { type Symbol; type Kind }
+// ^^^ variable.other.constant.scala
+// ^ punctuation.ascription.scala
+// ^^^^^^ support.type.scala
+// ^^^^^^ entity.name.type.scala
+
+import foo.*
+// ^ variable.language.wildcard.scala
+
+import foo.{*, given}
+// ^ variable.language.wildcard.scala
+// ^^^^^ storage.modifier.other.scala
+
+import foo.given T
+// ^ support.class.scala
+import foo.{given A, given B}
+// <- keyword.declaration.import.scala
+// ^^^^^ storage.modifier.other.scala
+// ^ support.class.scala
+// ^^^^^ storage.modifier.other.scala
+
+import Instances.{im, given Ordering[?]}
+// ^^^^^^^^ support.class.scala
+// ^ variable.language.hole.scala
+
+import Instances.given Ordering[?] with Other
+// ^^^^^^^^ support.class.scala
+// ^ variable.language.hole.scala
+// ^^^^ keyword.declaration.scala
+// ^^^^^ support.class.scala
+
+extension (c: Circle)
+// <- keyword.declaration.extension.scala
+// ^ punctuation.section.group.begin.scala
+// ^ variable.parameter.scala
+// ^^^^^^ support.class.scala
+// ^ punctuation.section.group.end.scala
+
+extension [T](xs: List[T])
+// <- keyword.declaration.extension.scala
+// ^ punctuation.definition.generic.begin.scala
+// ^ support.class.scala
+// ^ punctuation.definition.generic.end.scala
+// ^^ variable.parameter.scala
+// ^^^^ support.class.scala
+
+extension [T: Numeric](x: T)
+// <- keyword.declaration.extension.scala
+// ^ punctuation.definition.generic.begin.scala
+// ^ support.class.scala
+// ^ keyword.operator.bound.scala
+// ^^^^^^^ support.class.scala
+// ^ punctuation.definition.generic.end.scala
+// ^ variable.parameter.scala
+// ^ support.class.scala
+
+extension [T](x: T)(using n: Numeric[T])
+// ^^^^^ storage.modifier.other.scala
+// ^ variable.parameter.scala
+
+extension (i: Int) def isZero: Boolean = i == 0
+// ^^^ keyword.declaration.function.scala
+// ^^^^^^ entity.name.function.scala
+// ^ keyword.operator.assignment.scala
+
+enum Tree[T] derives Eq, Ordering, Show:
+// <- keyword.declaration.enum.scala
+// ^^^^ entity.name.enum.scala
+// ^ punctuation.definition.generic.begin.scala
+// ^ support.class.scala
+// ^ punctuation.definition.generic.end.scala
+// ^^^^^^^ storage.modifier.derives.scala
+// ^^ entity.other.inherited-class.scala
+// ^ punctuation.separator.scala
+// ^^^^^^^^ entity.other.inherited-class.scala
+// ^ punctuation.separator.scala
+// ^^^^ entity.other.inherited-class.scala
+// ^ punctuation.section.begin.scala
+
+class Foo extends Bar, Baz, Bin
+// ^^^ entity.other.inherited-class.scala
+// ^ punctuation.separator.scala
+// ^^^ entity.other.inherited-class.scala
+// ^ punctuation.separator.scala
+// ^^^ entity.other.inherited-class.scala
+
+
+x: [A] =>> Foo[A]
+// ^ punctuation.definition.generic.begin.scala
+// ^ support.class.scala
+// ^ punctuation.definition.generic.end.scala
+// ^^^ keyword.operator.arrow.type-lambda.scala
+// ^^^ support.class.scala
+
+x: [A, B[_], C] =>> Foo[A, B, C]
+// ^ punctuation.definition.generic.begin.scala
+// ^ support.class.scala
+// ^ variable.language.underscore.scala
+// ^^^ keyword.operator.arrow.type-lambda.scala
+// ^^^ support.class.scala
+
+type Y =
+ [A] =>> Foo
+// ^^^ keyword.operator.arrow.type-lambda.scala
+
+type Executable[T] = ExecutionContext ?=> T
+// ^^^ keyword.operator.arrow.type-context.scala
+
+opaque type Logarithm = Double
+// <- storage.modifier.other.scala
+// ^^^^ storage.type.scala
+
+type Foo = A & B
+// ^ keyword.operator.and.scala
+type Foo = A | B
+// ^ keyword.operator.or.scala
+
+case a: A | b: B => ()
+// ^ variable.parameter.scala
+// ^ keyword.operator.or.scala
+// ^ variable.parameter.scala
+
+{ x: A | B => () }
+// ^ keyword.operator.or.scala
+// ^ support.class.scala
+
+type F = (e: Entry, b: Other) => e.Key
+// ^ variable.parameter.scala
+// ^ punctuation.ascription.scala
+// ^ punctuation.separator.scala
+// ^^ keyword.operator.arrow.scala
+// ^ support.type.scala
+
+type F = [A] => List[A] => Seq[A]
+// ^^ keyword.operator.arrow.scala
+// ^^^^ support.class.scala
+// ^^ keyword.operator.arrow.scala
+// ^^^ support.class.scala
+
+type F = ((e: Entry, b: Other) => e.Key)
+// ^ variable.parameter.scala
+// ^ punctuation.ascription.scala
+// ^ punctuation.separator.scala
+// ^^ keyword.operator.arrow.scala
+// ^ support.type.scala
+
+type F = ([A] => List[A] => Seq[A])
+// ^^ keyword.operator.arrow.scala
+// ^^^^ support.class.scala
+// ^^ keyword.operator.arrow.scala
+// ^^^ support.class.scala
+
+type Elem[X] = X match
+// ^ support.class.scala
+// ^^^^^ keyword.control.flow.scala
+ case String => Foo
+// ^^^^ keyword.declaration.other.scala
+// ^^^^^^ support.class.scala
+// ^^ keyword.declaration.function.arrow.case.scala
+// ^^^ support.class.scala
+ case Array[t] => t
+// ^^^^ keyword.declaration.other.scala
+// ^^^^^ support.class.scala
+// ^ punctuation.definition.generic.begin.scala
+// ^ support.type.scala
+// ^ punctuation.definition.generic.end.scala
+// ^^ keyword.declaration.function.arrow.case.scala
+// ^ support.type.scala
+
+case Foo => ()
+// ^^^ support.constant.scala
+
+type Elem[X] = X match {
+// ^ support.class.scala
+// ^^^^^ keyword.control.flow.scala
+// ^ punctuation.definition.block.begin.scala
+
+
+ case String => Foo
+// ^^^^ keyword.declaration.other.scala
+// ^^^^^^ support.class.scala
+// ^^ keyword.declaration.function.arrow.case.scala
+// ^^^ support.class.scala
+
+
+ case Array[t] => t
+// ^^^^ keyword.declaration.other.scala
+// ^^^^^ support.class.scala
+// ^ punctuation.definition.generic.begin.scala
+// ^ support.type.scala
+// ^ punctuation.definition.generic.end.scala
+// ^^ keyword.declaration.function.arrow.case.scala
+// ^ support.type.scala
+
+
+}
+// <- punctuation.definition.block.end.scala
+
+trait Greeting(val name: String)
+// ^^^^ variable.parameter.scala
+
+class D extends C, Greeting("Bill")
+// ^^^^^^ string.quoted.double.scala
+
+class D extends C with Greeting("Bill")
+// ^^^^^^ string.quoted.double.scala
+
+trait ImpliedGreeting(using val iname: ImpliedName)
+// ^^^^^ storage.modifier.other
+
+transparent trait S
+// <- storage.modifier.other
+
+inline val x = 42
+// <- storage.modifier.other
+
+export foo.Bar
+// <- keyword.declaration.import.scala
+// ^^^ - support
+
+export foo.{bar as _, *}
+// <- keyword.declaration.import.scala
+// ^^ keyword.operator.as.scala
+// ^ variable.language.wildcard.scala
+
+import foo.{bar as _, *}
+// <- keyword.declaration.import.scala
+// ^^ keyword.operator.as.scala
+// ^ variable.language.wildcard.scala
+
+open class Writer
+// <- storage.modifier.other.scala
+
+val end = 42
+// ^^^ - keyword
+
+x + end
+// ^^^ - keyword
+
+val open = 42
+// ^^^^ - storage
+
+x + open
+// ^^^^ - storage
+
+x + infix
+// ^^^^ - storage
+
+x + opaque
+// ^^^^^^ - storage
+
+x + transparent
+// ^^^^^^^^^^^ - storage
+
+val derives = 42
+// ^^^^^^^ - storage
+
+x + derives
+// ^^^^^^^ - storage
+
+x + extension
+// ^^^^^^^^^ - keyword
+
+val x: 1 = 1
+// ^ constant.numeric.value.scala
+
+val x: 1f = 1f
+// ^ constant.numeric.value.scala
+// ^ constant.numeric.suffix.scala
+
+val x: 1d = 1d
+// ^ constant.numeric.value.scala
+// ^ constant.numeric.suffix.scala
+
+val x: true = true
+// ^^^^ constant.language.boolean.true.scala
+
+val x: F[true] = true
+// ^^^^ constant.language.boolean.true.scala
+// ^ punctuation.definition.generic.end.scala
+
+val c: 'c' = 'c'
+// ^^^ constant.character.literal.scala
+
+val str: "hi" = "hi"
+// ^^^ string.quoted.double.scala
+
+val str: """hi""" = """hi"""
+// ^^^^^^^^ string.quoted.triple.scala
+
+val i: 0x01 = 0x01
+// ^^ constant.numeric.base.scala
+// ^^ constant.numeric.value.scala
+
+type Foo = (true)
+// ^^^^ constant.language.boolean.true.scala
+// ^ punctuation.definition.group.end.scala
+
+enum Color {
+// <- keyword.declaration.enum.scala
+// ^^^^^ entity.name.enum.scala
+ case Red, Green, Blue
+// ^^^ keyword.declaration.other.scala
+// ^^^ entity.name.enum.scala
+// ^ punctuation.separator.scala
+// ^^^^^ entity.name.enum.scala
+// ^ punctuation.separator.scala
+// ^^^^ entity.name.enum.scala
+}
+
+enum Color:
+// <- keyword.declaration.enum.scala
+// ^^^^^ entity.name.enum.scala
+ case Red, Green, Blue
+// ^^^ keyword.declaration.other.scala
+// ^^^ entity.name.enum.scala
+// ^ punctuation.separator.scala
+// ^^^^^ entity.name.enum.scala
+// ^ punctuation.separator.scala
+// ^^^^ entity.name.enum.scala
+
+enum Color(val rgb: Int):
+// <- keyword.declaration.enum.scala
+// ^^^^^ entity.name.enum.scala
+// ^^^ storage.type.scala
+// ^^^ variable.parameter.scala
+// ^^^ storage.type.primitive.scala
+ case Red extends Color(0xFF0000)
+// ^^^ keyword.declaration.other.scala
+// ^^^ entity.name.enum.scala
+// ^^^^^^^ storage.modifier.extends.scala
+// ^^^^^ entity.other.inherited-class.scala
+ case Green extends Color(0x00FF00)
+// ^^^ keyword.declaration.other.scala
+// ^^^^^ entity.name.enum.scala
+// ^^^^^^^ storage.modifier.extends.scala
+// ^^^^^ entity.other.inherited-class.scala
+ case Blue extends Color(0x0000FF)
+// ^^^ keyword.declaration.other.scala
+// ^^^^ entity.name.enum.scala
+// ^^^^^^^ storage.modifier.extends.scala
+// ^^^^^ entity.other.inherited-class.scala
+
+enum Planet(mass: Double, radius: Double):
+// <- keyword.declaration.enum.scala
+// ^^^^^^ entity.name.enum.scala
+// ^^^^ variable.parameter.scala
+// ^^^^^^ variable.parameter.scala
+ private final val G = 6.67300E-11
+// ^^^^^^ storage.modifier.access.scala
+// ^^^^^ storage.modifier.other.scala
+// ^^^ storage.type.stable.scala
+// ^ variable.other.constant.scala
+ def surfaceGravity = G * mass / (radius * radius)
+// ^^ keyword.declaration.function.scala
+// ^^^^^^^^^^^^^^ entity.name.function.scala
+
+ case Mercury extends Planet(3.303e+23, 2.4397e6)
+// ^^^ keyword.declaration.other.scala
+// ^^^^^^^ entity.name.enum.scala
+// ^^^^^^^ storage.modifier.extends.scala
+// ^^^^^ entity.other.inherited-class.scala
+ case Venus extends Planet(4.869e+24, 6.0518e6)
+// ^^^ keyword.declaration.other.scala
+// ^^^^^ entity.name.enum.scala
+// ^^^^^^^ storage.modifier.extends.scala
+ case Earth extends Planet(5.976e+24, 6.37814e6)
+// ^^^ keyword.declaration.other.scala
+// ^^^^^ entity.name.enum.scala
+// ^^^^^^^ storage.modifier.extends.scala
+ case Mars extends Planet(6.421e+23, 3.3972e6)
+// ^^^ keyword.declaration.other.scala
+// ^^^^ entity.name.enum.scala
+// ^^^^^^^ storage.modifier.extends.scala
+ case Jupiter extends Planet(1.9e+27, 7.1492e7)
+// ^^^ keyword.declaration.other.scala
+// ^^^^^^^ entity.name.enum.scala
+// ^^^^^^^ storage.modifier.extends.scala
+ case Saturn extends Planet(5.688e+26, 6.0268e7)
+// ^^^ keyword.declaration.other.scala
+// ^^^^^^ entity.name.enum.scala
+// ^^^^^^^ storage.modifier.extends.scala
+ case Uranus extends Planet(8.686e+25, 2.5559e7)
+// ^^^ keyword.declaration.other.scala
+// ^^^^^^ entity.name.enum.scala
+// ^^^^^^^ storage.modifier.extends.scala
+ case Neptune extends Planet(1.024e+26, 2.4746e7)
+// ^^^ keyword.declaration.other.scala
+// ^^^^^^^ entity.name.enum.scala
+// ^^^^^^^ storage.modifier.extends.scala
+end Planet