From 9ce97e3d39479ef2307beb40860b52c2930bbd84 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sun, 29 Dec 2024 17:19:30 -0600 Subject: [PATCH 01/31] [Scala] Added support for new control syntax --- Scala/Scala.sublime-syntax | 55 ++++++++++++++++++++++++---------- Scala/syntax_test_scala.scala | 2 ++ Scala/syntax_test_scala3.scala | 38 +++++++++++++++++++++++ 3 files changed, 79 insertions(+), 16 deletions(-) create mode 100644 Scala/syntax_test_scala3.scala diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index be239a232d..f3112ffcf9 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -108,6 +108,7 @@ contexts: - include: storage-modifiers - include: declarations - include: for-comprehension + - include: if-statement - include: keywords - include: imports - include: strings @@ -969,29 +970,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: '(?=\n)' 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 - - include: for-parens-body + - 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 + - match: '(?=\S)' + set: + - match: '\byield\b' + scope: keyword.control.flow.scala + pop: true + - include: for-braces-body + for-braces-body: - match: |- (?x) - ^(?= + (?= ( [^<\x{2190}=\{\}/"] |<[^\-] @@ -1024,6 +1046,7 @@ contexts: - match: '(?=\))' pop: true - match: ; + scope: punctuation.terminator.scala pop: true - include: main diff --git a/Scala/syntax_test_scala.scala b/Scala/syntax_test_scala.scala index 7e255dc60c..f4a6195df8 100644 --- a/Scala/syntax_test_scala.scala +++ b/Scala/syntax_test_scala.scala @@ -2024,8 +2024,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 diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala new file mode 100644 index 0000000000..95829201f0 --- /dev/null +++ b/Scala/syntax_test_scala3.scala @@ -0,0 +1,38 @@ +// 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; From c4b1574e7e6e0a418360b4cd1cebb45894f312fe Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sun, 29 Dec 2024 17:44:00 -0600 Subject: [PATCH 02/31] [Scala] Implemented support for braceless block syntax (suffix colon) --- Scala/Scala.sublime-syntax | 32 +++++++++++++++++++++++++--- Scala/syntax_test_scala.scala | 38 +++++++++++++++++++--------------- Scala/syntax_test_scala3.scala | 27 ++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 20 deletions(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index f3112ffcf9..42e2ec0a8c 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -76,6 +76,8 @@ variables: # 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:]:_.-]*' @@ -173,6 +175,12 @@ contexts: - include: single-type-expression-no-function 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 @@ -230,13 +238,31 @@ 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: \( scope: punctuation.section.group.begin.scala diff --git a/Scala/syntax_test_scala.scala b/Scala/syntax_test_scala.scala index f4a6195df8..1024cbc540 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 diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index 95829201f0..4e7fb5f723 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -36,3 +36,30 @@ do 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 From b38ece515af3a98df6ee3d99f6f9aed6569802df Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sun, 29 Dec 2024 19:19:18 -0600 Subject: [PATCH 03/31] [Scala] Added support for `end` --- Scala/Scala.sublime-syntax | 41 +++++++++++++++++++++++++++- Scala/syntax_test_scala3.scala | 49 ++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index 42e2ec0a8c..1b258c1338 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|end|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' # lookaround for operators nonopchar: '[[[:alpha:]]0-9\s\(\)\[\]\{\}'']' @@ -466,6 +467,44 @@ contexts: - match: '(?=\}|\bcase\b)' # makes typing more pleasant pop: true - include: pattern-match + - match: '\bend\b' + scope: 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 case-body-first: - meta_content_scope: meta.block.case.first.scala diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index 4e7fb5f723..bbe1d8a4a2 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -63,3 +63,52 @@ 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 From 54d4d11a864e08e4cc713f9bc2a2263cf3f50c28 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sun, 29 Dec 2024 19:41:25 -0600 Subject: [PATCH 04/31] [Scala] Added tests for new wildcards --- Scala/syntax_test_scala3.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index bbe1d8a4a2..46f36e0e1d 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -112,3 +112,9 @@ end while end for // ^^^ keyword.control.flow.scala + +List[?] +// ^ variable.language.hole.scala +Map[? <: AnyRef, ? >: Null] +// ^ variable.language.hole.scala +// ^ variable.language.hole.scala From 70573850c21cbd47d0352b8d7d4309b3ad58b185 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sun, 29 Dec 2024 20:28:59 -0600 Subject: [PATCH 05/31] [Scala] Added support for `given` --- Scala/Scala.sublime-syntax | 111 +++++++++++++++++++++++-------- Scala/syntax_test_scala3.scala | 118 +++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+), 26 deletions(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index 1b258c1338..a3922f3fa8 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -17,7 +17,7 @@ first_line_match: |- variables: # all reserved words (https://www.safaribooksonline.com/library/view/learning-scala/9781449368814/apa.html) # note that this omits conditionally reserved words - keywords: '\b(?:abstract|case|catch|class|def|do|end|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' + keywords: '\b(?:abstract|case|catch|class|def|do|end|else|extends|false|final|finally|for|forSome|given|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' # lookaround for operators nonopchar: '[[[:alpha:]]0-9\s\(\)\[\]\{\}'']' @@ -467,6 +467,24 @@ contexts: - match: '(?=\}|\bcase\b)' # makes typing more pleasant pop: true - include: pattern-match + - match: '\bgiven\b' + scope: keyword.declaration.given.scala + push: + - 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: '\bend\b' scope: keyword.control.section.end.scala push: @@ -644,30 +662,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 @@ -676,6 +671,38 @@ contexts: - match: '(?=\S)' pop: true + function-parameter-list-contents: + - match: '\)' + scope: punctuation.section.group.end.scala + pop: true + - match: '\busing\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 + - include: main + function-parameter-list-newline: - include: decl-newline-double-check - match: '(?=\S)' @@ -915,6 +942,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 @@ -931,6 +972,18 @@ contexts: scope: punctuation.separator.scala - match: '{{rightarrow}}' scope: keyword.operator.arrow.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 @@ -1178,7 +1231,7 @@ 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 + - match: \b(?:abstract|final|lazy|sealed|implicit|override|using)\b scope: storage.modifier.other.scala # see http://www.scala-lang.org/docu/files/ScalaReference.pdf part 1.3.5-6 (page 18) @@ -1597,6 +1650,12 @@ contexts: - match: '[`\n]' scope: punctuation.definition.identifier.scala pop: true + - match: '\bgiven\b' + scope: keyword.declaration.given.scala + push: + - match: '(?=[,)]|{{rightarrow}}|<-|\x{2190}|=|do\b|yield\b)' + pop: true + - include: delimited-type-expression - match: '{{varid}}(?:(\.){{varid}})+' captures: 1: punctuation.accessor.scala diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index 46f36e0e1d..681fdb6fdd 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -118,3 +118,121 @@ List[?] 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 From fdf4e8a06af58416101baf7ed0ff9dacf09f4d95 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 08:09:13 -0600 Subject: [PATCH 06/31] [Scala] Added support for extensions --- Scala/Scala.sublime-syntax | 11 +++++++++++ Scala/syntax_test_scala3.scala | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index a3922f3fa8..3c41fb7921 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -523,6 +523,17 @@ contexts: pop: true - match: (?=\S) pop: true + - match: '\bextension\b' + 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 case-body-first: - meta_content_scope: meta.block.case.first.scala diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index 681fdb6fdd..a98678466e 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -236,3 +236,37 @@ import Instances.given Ordering[?] with Other // ^ 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 From 6b34a212d3cd2b88cd48c371a92747cb363bff2d Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 08:24:32 -0600 Subject: [PATCH 07/31] [Scala] Added support for enums --- Scala/Scala.sublime-syntax | 44 ++++++++++++++++++++++++++++------ Scala/syntax_test_scala3.scala | 21 ++++++++++++++++ 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index 3c41fb7921..53094005c3 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -17,7 +17,7 @@ first_line_match: |- variables: # all reserved words (https://www.safaribooksonline.com/library/view/learning-scala/9781449368814/apa.html) # note that this omits conditionally reserved words - keywords: '\b(?:abstract|case|catch|class|def|do|end|else|extends|false|final|finally|for|forSome|given|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' + keywords: '\b(?:abstract|case|catch|class|def|derives|do|end|else|enum|extends|extension|false|final|finally|for|forSome|given|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' # lookaround for operators nonopchar: '[[[:alpha:]]0-9\s\(\)\[\]\{\}'']' @@ -534,6 +534,28 @@ contexts: 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: '\[' + 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 case-body-first: - meta_content_scope: meta.block.case.first.scala @@ -686,7 +708,7 @@ contexts: - match: '\)' scope: punctuation.section.group.end.scala pop: true - - match: '\busing\b' + - match: '\b(?:using|implicit)\b' scope: storage.modifier.other.scala - match: '({{alphaid}})(?=\s*:)' captures: @@ -712,7 +734,12 @@ contexts: - match: '(?=[,)])' pop: true - include: delimited-type-expression - - include: main + - match: '=' + scope: keyword.operator.assignment.scala + push: + - match: '(?=[,)])' + pop: true + - include: main function-parameter-list-newline: - include: decl-newline-double-check @@ -865,7 +892,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: @@ -889,13 +916,16 @@ 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: '(?=\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: diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index a98678466e..e7b5795033 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -270,3 +270,24 @@ 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 From 67080b0371fb43ac2f538dd386d5f2d21c00d6ae Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 09:25:12 -0600 Subject: [PATCH 08/31] [Scala] Added support for type lambdas and cleaned up broken things --- Scala/Scala.sublime-syntax | 45 +++++++--------------------------- Scala/syntax_test_scala.scala | 11 --------- Scala/syntax_test_scala3.scala | 19 ++++++++++++++ 3 files changed, 28 insertions(+), 47 deletions(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index 53094005c3..922a1cd0fc 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -456,14 +456,14 @@ contexts: set: - match: '{{rightarrow}}' scope: keyword.declaration.function.arrow.case.scala - set: case-body-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-first + pop: true - match: '(?=\}|\bcase\b)' # makes typing more pleasant pop: true - include: pattern-match @@ -557,30 +557,6 @@ contexts: - match: '(?=\S)' pop: true - 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 - set: - - meta_scope: meta.block.scala - - match: \} - scope: punctuation.section.block.end.scala - set: case-body-non-first - - include: main - - include: case-body - # this exists to clear the meta_scope from first or non-first case-pattern: - match: '\bcase\b' @@ -592,25 +568,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 @@ -1164,7 +1133,7 @@ contexts: pop: true - match: '(?=\S)' set: - - match: '\byield\b' + - match: '\b(?:yield|do)\b' scope: keyword.control.flow.scala pop: true - include: for-braces-body @@ -1792,6 +1761,10 @@ 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.declaration.function.arrow.type-lambda.scala base-type-expression: - include: base-type-expression-no-function diff --git a/Scala/syntax_test_scala.scala b/Scala/syntax_test_scala.scala index 1024cbc540..3912338acf 100644 --- a/Scala/syntax_test_scala.scala +++ b/Scala/syntax_test_scala.scala @@ -1644,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 diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index e7b5795033..4c27fe8bb9 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -291,3 +291,22 @@ class Foo extends Bar, Baz, Bin // ^^^ 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.declaration.function.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.declaration.function.arrow.type-lambda.scala +// ^^^ support.class.scala + +type Y = + [A] =>> Foo +// ^^^ keyword.declaration.function.arrow.type-lambda.scala From 893ba90ce5d69a82441013748334afa71601ceab Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 09:29:07 -0600 Subject: [PATCH 09/31] [Scala] Added support for context function types and fixed scoping --- Scala/Scala.sublime-syntax | 5 ++++- Scala/syntax_test_scala3.scala | 9 ++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index 922a1cd0fc..788c1ef883 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -1764,7 +1764,10 @@ contexts: # the whitespace covering is required to deal with the very aggressive scope popping for single type exprs - match: '\s*(=>>)\s*' captures: - 1: keyword.declaration.function.arrow.type-lambda.scala + 1: keyword.operator.arrow.type-lambda.scala + - match: '\s*(\?=>)\s*' + captures: + 1: keyword.operator.arrow.type-context.scala base-type-expression: - include: base-type-expression-no-function diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index 4c27fe8bb9..fb66b46e64 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -297,16 +297,19 @@ x: [A] =>> Foo[A] // ^ punctuation.definition.generic.begin.scala // ^ support.class.scala // ^ punctuation.definition.generic.end.scala -// ^^^ keyword.declaration.function.arrow.type-lambda.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.declaration.function.arrow.type-lambda.scala +// ^^^ keyword.operator.arrow.type-lambda.scala // ^^^ support.class.scala type Y = [A] =>> Foo -// ^^^ keyword.declaration.function.arrow.type-lambda.scala +// ^^^ keyword.operator.arrow.type-lambda.scala + +type Executable[T] = ExecutionContext ?=> T +// ^^^ keyword.operator.arrow.type-context.scala From 4580c2cd380926292978a258d60f04e70cd1b2c1 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 09:54:36 -0600 Subject: [PATCH 10/31] [Scala] Implemented support for opaque types and intersect/unions --- Scala/Scala.sublime-syntax | 132 +++++++++++++++++++++++++++++++-- Scala/syntax_test_scala3.scala | 18 +++++ 2 files changed, 142 insertions(+), 8 deletions(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index 788c1ef883..cac347bccf 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -17,7 +17,7 @@ first_line_match: |- variables: # all reserved words (https://www.safaribooksonline.com/library/view/learning-scala/9781449368814/apa.html) # note that this omits conditionally reserved words - keywords: '\b(?:abstract|case|catch|class|def|derives|do|end|else|enum|extends|extension|false|final|finally|for|forSome|given|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' + keywords: '\b(?:abstract|case|catch|class|def|derives|do|end|else|enum|extends|extension|false|final|finally|for|forSome|given|if|implicit|import|lazy|match|new|null|object|opaque|override|package|private|protected|return|sealed|super|this|throw|trait|true|try|type|val|var|while|with|yield)\b' # lookaround for operators nonopchar: '[[[:alpha:]]0-9\s\(\)\[\]\{\}'']' @@ -166,14 +166,14 @@ contexts: 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 @@ -1241,7 +1241,7 @@ 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|using)\b + - match: \b(?:abstract|final|lazy|sealed|implicit|opaque|override|using)\b scope: storage.modifier.other.scala # see http://www.scala-lang.org/docu/files/ScalaReference.pdf part 1.3.5-6 (page 18) @@ -1663,7 +1663,7 @@ contexts: - match: '\bgiven\b' scope: keyword.declaration.given.scala push: - - match: '(?=[,)]|{{rightarrow}}|<-|\x{2190}|=|do\b|yield\b)' + - match: '(?=[,)]|{{rightarrow}}|<-|\x{2190}|=|\bdo\b|\byield\b)' pop: true - include: delimited-type-expression - match: '{{varid}}(?:(\.){{varid}})+' @@ -1713,7 +1713,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: \( @@ -1725,7 +1726,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: @@ -1734,7 +1735,7 @@ contexts: pop: true - include: nested-pattern-match - base-type-expression-no-function: + base-type-expression-no-function-no-or: - match: ; scope: punctuation.terminator.scala pop: true @@ -1768,6 +1769,15 @@ contexts: - 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 @@ -1818,6 +1828,40 @@ contexts: - 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 + # - include: literal-constants + # - include: char-literal + # - include: scala-symbol + # - include: strings + - 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 @@ -1965,6 +2009,67 @@ 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 + # - include: literal-constants + # - include: char-literal + # - include: scala-symbol + # - include: strings + - 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: + - 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: @@ -2037,6 +2142,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 @@ -2048,6 +2159,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_scala3.scala b/Scala/syntax_test_scala3.scala index fb66b46e64..9a42f5bacb 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -313,3 +313,21 @@ type Y = 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 From cf57ad58cafbc598f29ad0f3f4866e2b59b2f3bc Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 10:11:45 -0600 Subject: [PATCH 11/31] [Scala] Added support for dependent function types --- Scala/Scala.sublime-syntax | 30 ++++++++++++++++++++++++++++-- Scala/syntax_test_scala3.scala | 6 ++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index cac347bccf..32a8bc2030 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -1926,10 +1926,21 @@ contexts: set: single-type-expression-tail - match: (?=@{{plainid}}) set: single-type-expression-tail - - match: \( + - match: '(?={{withinparens}}\s*{{rightarrow}})' + push: + - match: '\(' + scope: punctuation.definition.group.begin.scala + push: dependent-type-parameters + - match: '({{rightarrow}})\s*' + captures: + 1: keyword.operator.arrow.scala + pop: true + - match: '(?=\S)' + pop: true + - 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 @@ -1937,6 +1948,21 @@ contexts: - match: '(?=.)' # we can be SUPER aggressive about popping out here pop: true + dependent-type-parameters: + - match: '\)' + scope: punctuation.definition.group.end.scala + pop: true + - match: '{{id}}' + scope: variable.parameter.scala + - match: ',' + scope: punctuation.separator.scala + - match: ':' + scope: punctuation.ascription.scala + set: + - match: '(?=[,)])' + set: dependent-type-parameters + - include: delimited-type-expression + # single-type-expression, but with an allowance for one *leading* newline and whitespace single-type-expression-leading-newline: - match: \n diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index 9a42f5bacb..de8263709c 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -331,3 +331,9 @@ case a: A | b: B => () { x: A | B => () } // ^ keyword.operator.or.scala // ^ support.class.scala + +type F = (e: Entry, b: Other) => e.Key +// ^ variable.parameter.scala +// ^ punctuation.ascription.scala +// ^^ keyword.operator.arrow.scala +// ^ support.type.scala From 921ba9da595d472e3bc9567ac894ace41bda6c80 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 10:14:00 -0600 Subject: [PATCH 12/31] [Scala] Fixed missing punctuation scope for lambda parameters --- Scala/Scala.sublime-syntax | 6 ++++-- Scala/syntax_test_scala.scala | 2 ++ Scala/syntax_test_scala3.scala | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index 32a8bc2030..f13e9e1907 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -265,15 +265,17 @@ contexts: 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: diff --git a/Scala/syntax_test_scala.scala b/Scala/syntax_test_scala.scala index 3912338acf..d69eafe076 100644 --- a/Scala/syntax_test_scala.scala +++ b/Scala/syntax_test_scala.scala @@ -2298,3 +2298,5 @@ completed: F[_ >: A] => B) // ^ entity.name.type.scala } +(abc, cba) => () +// ^ punctuation.separator.scala diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index de8263709c..90e79b0733 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -335,5 +335,6 @@ case a: A | b: B => () type F = (e: Entry, b: Other) => e.Key // ^ variable.parameter.scala // ^ punctuation.ascription.scala +// ^ punctuation.separator.scala // ^^ keyword.operator.arrow.scala // ^ support.type.scala From e224dee2cc30da67153cc5502d617c414b6204b2 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 10:37:29 -0600 Subject: [PATCH 13/31] [Scala] Added support for type lambdas and cleaned up dependent types --- Scala/Scala.sublime-syntax | 41 +++++++++++++--------------------- Scala/syntax_test_scala3.scala | 19 ++++++++++++++++ 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index f13e9e1907..9921e82140 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -71,7 +71,7 @@ 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 @@ -1747,7 +1747,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: @@ -1928,42 +1928,31 @@ contexts: set: single-type-expression-tail - match: (?=@{{plainid}}) set: single-type-expression-tail - - match: '(?={{withinparens}}\s*{{rightarrow}})' - push: - - match: '\(' - scope: punctuation.definition.group.begin.scala - push: dependent-type-parameters - - match: '({{rightarrow}})\s*' - captures: - 1: keyword.operator.arrow.scala - pop: true - - match: '(?=\S)' - pop: true + - match: '\[' + scope: punctuation.definition.generic.begin.scala + set: + - 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: '\)' 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 - dependent-type-parameters: - - match: '\)' - scope: punctuation.definition.group.end.scala - pop: true - - match: '{{id}}' - scope: variable.parameter.scala - - match: ',' - scope: punctuation.separator.scala + delimited-type-group-expression: + - match: '({{id}})(?=\s*:)' + captures: + 1: variable.parameter.scala - match: ':' scope: punctuation.ascription.scala - set: - - match: '(?=[,)])' - set: dependent-type-parameters - - include: delimited-type-expression + - include: delimited-type-expression # single-type-expression, but with an allowance for one *leading* newline and whitespace single-type-expression-leading-newline: diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index 90e79b0733..80fe12b06f 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -338,3 +338,22 @@ type F = (e: Entry, b: Other) => e.Key // ^ 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 From aef5cd3165ee67da740d9b0ba9d927df225bb196 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 13:26:11 -0600 Subject: [PATCH 14/31] [Scala] Added support for match types (grumble grumble) --- Scala/Scala.sublime-syntax | 65 +++++++++++++++++++++++++++++++++- Scala/syntax_test_scala3.scala | 46 ++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index 9921e82140..d2bb97da74 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -161,7 +161,7 @@ contexts: scope: constant.character.literal.scala comments: - - match: (//).*$ + - match: '(//).*\n' scope: comment.line.double-slash.scala captures: 1: punctuation.definition.comment.scala @@ -2122,6 +2122,14 @@ 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 @@ -2152,6 +2160,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 diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index 80fe12b06f..268d2a082f 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -357,3 +357,49 @@ type F = ([A] => List[A] => Seq[A]) // ^^^^ 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 From ef5000403e9feb0d849d69e06efd8fc56ef2eae5 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 13:31:09 -0600 Subject: [PATCH 15/31] [Scala] Added tests for trait parameters --- Scala/Scala.sublime-syntax | 2 +- Scala/syntax_test_scala3.scala | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index d2bb97da74..f513350814 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -161,7 +161,7 @@ contexts: scope: constant.character.literal.scala comments: - - match: '(//).*\n' + - match: '(//).*\n?$' scope: comment.line.double-slash.scala captures: 1: punctuation.definition.comment.scala diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index 268d2a082f..6451db0ac6 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -403,3 +403,15 @@ type Elem[X] = X match { } // <- 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 From 8779b391ac81c60b686b2dd0d9964494c67ca56e Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 13:33:37 -0600 Subject: [PATCH 16/31] [Scala] Added support for `transparent` and `inline` --- Scala/Scala.sublime-syntax | 4 ++-- Scala/syntax_test_scala3.scala | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index f513350814..d19cc43a98 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -17,7 +17,7 @@ first_line_match: |- variables: # all reserved words (https://www.safaribooksonline.com/library/view/learning-scala/9781449368814/apa.html) # note that this omits conditionally reserved words - keywords: '\b(?:abstract|case|catch|class|def|derives|do|end|else|enum|extends|extension|false|final|finally|for|forSome|given|if|implicit|import|lazy|match|new|null|object|opaque|override|package|private|protected|return|sealed|super|this|throw|trait|true|try|type|val|var|while|with|yield)\b' + keywords: '\b(?:abstract|case|catch|class|def|derives|do|end|else|enum|extends|extension|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\(\)\[\]\{\}'']' @@ -1243,7 +1243,7 @@ contexts: storage-modifiers: - match: '\b(?:private\[\S+\]|protected\[\S+\]|private|protected)\b' scope: storage.modifier.access.scala - - match: \b(?:abstract|final|lazy|sealed|implicit|opaque|override|using)\b + - match: '\b(?:abstract|final|lazy|sealed|transparent|inline|implicit|opaque|override|using)\b' scope: storage.modifier.other.scala # see http://www.scala-lang.org/docu/files/ScalaReference.pdf part 1.3.5-6 (page 18) diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index 6451db0ac6..d6abb70096 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -415,3 +415,9 @@ class D extends C with Greeting("Bill") trait ImpliedGreeting(using val iname: ImpliedName) // ^^^^^ storage.modifier.other + +transparent trait S +// <- storage.modifier.other + +inline val x = 42 +// <- storage.modifier.other From c24958430dd8b82e66fc865d49d71e8b2adc20e4 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 13:39:01 -0600 Subject: [PATCH 17/31] [Scala] Added support for `export` and `as` renames --- Scala/Scala.sublime-syntax | 4 +++- Scala/syntax_test_scala3.scala | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index d19cc43a98..0ba8200c72 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -943,7 +943,7 @@ contexts: set: class-inheritance-with imports: - - match: \bimport\b + - match: '\b(?:import|export)\b' scope: keyword.declaration.import.scala push: - meta_scope: meta.import.scala @@ -984,6 +984,8 @@ 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: diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index d6abb70096..8fd233f0cc 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -421,3 +421,17 @@ transparent trait S 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 From fe5a91529808f196972654919e9c0eb4e42c8654 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 13:57:20 -0600 Subject: [PATCH 18/31] [Scala] Better handling of soft modifiers --- Scala/Scala.sublime-syntax | 12 ++++++++---- Scala/syntax_test_scala3.scala | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index 0ba8200c72..68d89c7bcc 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -17,7 +17,7 @@ first_line_match: |- variables: # all reserved words (https://www.safaribooksonline.com/library/view/learning-scala/9781449368814/apa.html) # note that this omits conditionally reserved words - keywords: '\b(?:abstract|case|catch|class|def|derives|do|end|else|enum|extends|extension|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' + keywords: '\b(?:abstract|case|catch|class|def|do|else|enum|extends|extension|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\(\)\[\]\{\}'']' @@ -487,8 +487,9 @@ contexts: push: function-parameter-list-contents - match: '(?=\S)' pop: true - - match: '\bend\b' - scope: keyword.control.section.end.scala + - match: '^\s*(end)\b' + captures: + 1: keyword.control.section.end.scala push: - match: '\bextension\b' scope: keyword.declaration.extension.scala @@ -1245,7 +1246,10 @@ contexts: storage-modifiers: - match: '\b(?:private\[\S+\]|protected\[\S+\]|private|protected)\b' scope: storage.modifier.access.scala - - match: '\b(?:abstract|final|lazy|sealed|transparent|inline|implicit|opaque|override|using)\b' + - match: '\b(?:abstract|final|lazy|sealed|implicit|override|using)\b' + scope: storage.modifier.other.scala + # soft modifiers + - match: '\b(?:infix|inline|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)\b)' scope: storage.modifier.other.scala # see http://www.scala-lang.org/docu/files/ScalaReference.pdf part 1.3.5-6 (page 18) diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index 8fd233f0cc..42ab9b6d50 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -435,3 +435,36 @@ 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 + inline +// ^^^^^^ - storage + +x + opaque +// ^^^^^^ - storage + +x + transparent +// ^^^^^^^^^^^ - storage + +val derives = 42 +// ^^^^^^^ - storage + +x + derives +// ^^^^^^^ - storage From fb34a444bbca7ebad0deed03f5a732ab0bdd465d Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 14:03:42 -0600 Subject: [PATCH 19/31] [Scala] Still-better soft modifier handling --- Scala/Scala.sublime-syntax | 9 +++++---- Scala/syntax_test_scala3.scala | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index 68d89c7bcc..4a8722630d 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -17,7 +17,7 @@ first_line_match: |- variables: # all reserved words (https://www.safaribooksonline.com/library/view/learning-scala/9781449368814/apa.html) # note that this omits conditionally reserved words - keywords: '\b(?:abstract|case|catch|class|def|do|else|enum|extends|extension|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' + 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\(\)\[\]\{\}'']' @@ -526,7 +526,7 @@ contexts: pop: true - match: (?=\S) pop: true - - match: '\bextension\b' + - match: '\bextension\b(?=\s*[(\[])' scope: keyword.declaration.extension.scala push: - match: '\[' @@ -1246,10 +1246,11 @@ 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|using)\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|inline|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)\b)' + - 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) diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index 42ab9b6d50..ed1ec11b5a 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -454,9 +454,6 @@ x + open x + infix // ^^^^ - storage -x + inline -// ^^^^^^ - storage - x + opaque // ^^^^^^ - storage @@ -468,3 +465,6 @@ val derives = 42 x + derives // ^^^^^^^ - storage + +x + extension +// ^^^^^^^^^ - keyword From 720b2ebaeaef25681c587f23b2f68a5812f8cbf3 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 15:35:34 -0600 Subject: [PATCH 20/31] [Scala] Added support for literal types --- Scala/Scala.sublime-syntax | 49 +++++++++++----------------------- Scala/syntax_test_scala3.scala | 35 ++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index 4a8722630d..93505f2780 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -303,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: |- @@ -341,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 @@ -1254,7 +1259,7 @@ contexts: 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: @@ -1276,6 +1281,9 @@ contexts: - match: \n scope: invalid.string.newline.scala - include: escaped + + strings: + - include: base-strings - match: '(f)(""")' captures: 1: support.function.scala @@ -1745,6 +1753,9 @@ contexts: - include: nested-pattern-match base-type-expression-no-function-no-or: + - include: literal-constants-base + - include: char-literal + - include: base-strings - match: ; scope: punctuation.terminator.scala pop: true @@ -1798,6 +1809,7 @@ contexts: scope: keyword.operator.bound.scala delimited-type-expression: + - include: base-type-expression - include: annotation # kind-projector support - match: '([+-])?([?*])' @@ -1815,10 +1827,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 @@ -1834,7 +1842,6 @@ 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: @@ -1851,10 +1858,6 @@ contexts: - match: '{{upperid}}' scope: support.class.scala set: single-type-expression-tail-no-function-no-or - # - include: literal-constants - # - include: char-literal - # - include: scala-symbol - # - include: strings - match: '{{typeid}}' scope: support.type.scala set: single-type-expression-tail-no-function-no-or @@ -1885,10 +1888,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 @@ -1924,10 +1923,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}}' @@ -2009,10 +2004,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}}' @@ -2067,10 +2058,6 @@ contexts: - match: '{{upperid}}' scope: support.class.scala set: single-type-expression-tail-no-function-no-or-type-expectation - # - include: literal-constants - # - include: char-literal - # - include: scala-symbol - # - include: strings - match: '(?={{keywords}})' pop: true # we need to explicitly match this one because "|" is a valid type name @@ -2140,10 +2127,6 @@ contexts: - 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}}' diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index ed1ec11b5a..2846ba9220 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -468,3 +468,38 @@ x + derives 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 From cd30d5c9165fefd15c1ee5855aa960342d539365 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Mon, 30 Dec 2024 15:45:48 -0600 Subject: [PATCH 21/31] [Scala] Updated indentation rules to deal with braceless things as best we can --- Scala/Dedentation Rules - case.tmPreferences | 17 ------------ Scala/Indentation Rules.tmPreferences | 28 +++++++++++--------- 2 files changed, 16 insertions(+), 29 deletions(-) delete mode 100644 Scala/Dedentation Rules - case.tmPreferences 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..7203038cb8 100644 --- a/Scala/Indentation Rules.tmPreferences +++ b/Scala/Indentation Rules.tmPreferences @@ -9,23 +9,27 @@ (?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 From a3f18c9b256820595dfab25e53b8499d1bd2f248 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Tue, 31 Dec 2024 08:15:04 -0600 Subject: [PATCH 22/31] [Scala] Added test for #3778 --- Scala/syntax_test_scala.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Scala/syntax_test_scala.scala b/Scala/syntax_test_scala.scala index d69eafe076..2f72fc73e4 100644 --- a/Scala/syntax_test_scala.scala +++ b/Scala/syntax_test_scala.scala @@ -2300,3 +2300,12 @@ completed: F[_ >: A] => B) (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 From 3097120aaa8ac5c57246bc10b92bf572ffb2ad94 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Tue, 31 Dec 2024 08:15:49 -0600 Subject: [PATCH 23/31] [Scala] Inverted the order of comment newline eating --- Scala/Scala.sublime-syntax | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index 93505f2780..e0cb2ee394 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -161,7 +161,7 @@ contexts: scope: constant.character.literal.scala comments: - - match: '(//).*\n?$' + - match: '(//).*$\n?' scope: comment.line.double-slash.scala captures: 1: punctuation.definition.comment.scala From 4ed4deddcfa48deeaa42c4d6268bff1aa8ace176 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Fri, 3 Jan 2025 16:04:45 -0600 Subject: [PATCH 24/31] [Scala] Added support for `enum` --- Scala/Scala.sublime-syntax | 183 ++++++++++++++++++++++++++++----- Scala/syntax_test_scala3.scala | 94 +++++++++++++++++ 2 files changed, 252 insertions(+), 25 deletions(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index e0cb2ee394..a2536c538e 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -390,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: @@ -397,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 @@ -451,29 +459,6 @@ 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 - push: - - meta_content_scope: meta.pattern.scala - - match: '(?=\bclass\b)' - pop: true - - match: '\bif\b' - scope: keyword.control.flow.scala - set: - - match: '{{rightarrow}}' - scope: keyword.declaration.function.arrow.case.scala - 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 - pop: true - - match: '(?=\}|\bcase\b)' # makes typing more pleasant - pop: true - - include: pattern-match - match: '\bgiven\b' scope: keyword.declaration.given.scala push: @@ -565,7 +550,49 @@ contexts: - match: '(?=\S)' pop: true - # this exists to clear the meta_scope from first or non-first + # 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: + - 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 + case-pattern: - match: '\bcase\b' scope: keyword.declaration.other.scala @@ -736,13 +763,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: @@ -750,6 +779,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: '\[' @@ -794,6 +858,8 @@ contexts: - match: '(?=[=])' pop: true - include: main + - match: '(?=\bderives\b)' + set: enum-inheritance-derives - match: '(?=\b(?:extends|with)\b)' set: class-inheritance-extends - match: '(?=\{)' @@ -808,6 +874,57 @@ contexts: - match: '(?=\S)' set: class-parameter-list + class-parameter-list-maybe-case: + - match: '{{rightarrow}}' + fail: case + - match: '\(' + scope: punctuation.section.group.begin.scala + push: + - 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: - match: \{ scope: punctuation.section.braces.begin.scala @@ -817,6 +934,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)' @@ -896,6 +1015,8 @@ contexts: - 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: '(?=\{)' @@ -919,6 +1040,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)' @@ -948,6 +1071,16 @@ 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: '\b(?:import|export)\b' scope: keyword.declaration.import.scala diff --git a/Scala/syntax_test_scala3.scala b/Scala/syntax_test_scala3.scala index 2846ba9220..d1ba964c0d 100644 --- a/Scala/syntax_test_scala3.scala +++ b/Scala/syntax_test_scala3.scala @@ -503,3 +503,97 @@ val i: 0x01 = 0x01 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 From 7dd124b47674b9745bfa3c97e0647440c776b218 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Fri, 3 Jan 2025 16:07:38 -0600 Subject: [PATCH 25/31] [Scala] Made the typing experience slightly more pleasant for braceless if/else and similar --- Scala/Indentation Rules.tmPreferences | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Scala/Indentation Rules.tmPreferences b/Scala/Indentation Rules.tmPreferences index 7203038cb8..dc54e9308e 100644 --- a/Scala/Indentation Rules.tmPreferences +++ b/Scala/Indentation Rules.tmPreferences @@ -29,7 +29,8 @@ (?x) ^ (.*\*/)? \s* \} .* $ # curly brace dedent | ^ (.*\*/)? \s* \) .* $ # parent dedent - | ^ \s* end ($|\s+ .* $) # end token + | ^ \s* end ($|\s+ .* $) # end token + | ^ \s* (catch|else|yield) ($|\s+ .* $) # braceless things (I would prefer to also do while/do but we can't) From 1687e44a2bb2bbf1a9a8c61afc76e9950d78e6dc Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Fri, 3 Jan 2025 16:12:54 -0600 Subject: [PATCH 26/31] [Scala] Fixed self types for newly restrictive lambda syntax --- Scala/Scala.sublime-syntax | 3 +++ Scala/syntax_test_scala.scala | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index a2536c538e..0141fa4c5c 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -926,6 +926,9 @@ contexts: 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: diff --git a/Scala/syntax_test_scala.scala b/Scala/syntax_test_scala.scala index 2f72fc73e4..4463cb7a93 100644 --- a/Scala/syntax_test_scala.scala +++ b/Scala/syntax_test_scala.scala @@ -2309,3 +2309,18 @@ class c() // ^^^ 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 From 677ce0d2f27b765a3f07265becbeae7c286a4a49 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sun, 5 Jan 2025 11:30:25 -0600 Subject: [PATCH 27/31] Update Scala/Scala.sublime-syntax Co-authored-by: deathaxe --- Scala/Scala.sublime-syntax | 1 + 1 file changed, 1 insertion(+) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index 0141fa4c5c..94111d74cf 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -880,6 +880,7 @@ contexts: - match: '\(' scope: punctuation.section.group.begin.scala push: + - meta_scope: meta.group.scala - match: '\)' scope: punctuation.section.group.end.scala pop: true From f8573cc302c47accc49afcd4bf709cb828144125 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sun, 5 Jan 2025 11:33:26 -0600 Subject: [PATCH 28/31] Update Scala/Scala.sublime-syntax Co-authored-by: deathaxe --- Scala/Scala.sublime-syntax | 1 + 1 file changed, 1 insertion(+) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index 94111d74cf..c38cd99b51 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -2206,6 +2206,7 @@ contexts: - 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 From e22aa770d0054901b07d9fa8efbd696ea48504e4 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sun, 5 Jan 2025 11:33:36 -0600 Subject: [PATCH 29/31] Update Scala/Scala.sublime-syntax Co-authored-by: deathaxe --- Scala/Scala.sublime-syntax | 1 + 1 file changed, 1 insertion(+) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index c38cd99b51..cc4190ce62 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -2070,6 +2070,7 @@ contexts: - 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 From 1a6f466b509aa11edcc8ed4f4eb2ef326541656c Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sun, 5 Jan 2025 11:34:25 -0600 Subject: [PATCH 30/31] Update Scala/Scala.sublime-syntax Co-authored-by: deathaxe --- Scala/Scala.sublime-syntax | 1 + 1 file changed, 1 insertion(+) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index cc4190ce62..aac5f97daf 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -709,6 +709,7 @@ contexts: pop: true function-parameter-list-contents: + - meta_scope: meta.group.scala - match: '\)' scope: punctuation.section.group.end.scala pop: true From 80feed5436251b5c85a0958c31e93aae82fcf98c Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sun, 5 Jan 2025 10:52:49 -0700 Subject: [PATCH 31/31] Update Scala/Scala.sublime-syntax Co-authored-by: deathaxe --- Scala/Scala.sublime-syntax | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scala/Scala.sublime-syntax b/Scala/Scala.sublime-syntax index aac5f97daf..0c7d32d5db 100644 --- a/Scala/Scala.sublime-syntax +++ b/Scala/Scala.sublime-syntax @@ -1250,7 +1250,7 @@ contexts: - match: '\bif\b' scope: keyword.control.flow.scala push: - - match: '(?=\n)' + - match: '$' pop: true - match: '\bthen\b' scope: keyword.control.flow.scala