changeset 34445:5609b011fd70

runtime(java): Recognise _when_ clauses in _switch_ blocks Commit: https://github.com/vim/vim/commit/9ecf02cd5f5feabe096f66cd5f503a822c78793f Author: Aliaksei Budavei <0x000c70@gmail.com> Date: Wed Feb 28 21:30:22 2024 +0100 runtime(java): Recognise _when_ clauses in _switch_ blocks Also: - distinguish _yield_ when used as a contextual keyword from when used qualified as a method or a method reference (as can be seen in testdir/input/java_switch.java, variables and method declarations named _yield_ will be recognised as the namesake keyword--consider picking other names for variables, and defining g:java_highlight_functions to have method names painted; since _yield_ statements can have trailing parens, they must be recognised as statements, for only qualified _yield_ method calls are supported); - recognise grouped _default_ _case_ labels; - describe primitive types for _case_ labels (JLS, ?14.11, ?3.10.1); - recognise some non-ASCII identifiers (see javaLambdaDef, javaUserLabel) (further improvement for better recognition of identifiers will be arranged in a separate PR). Because the arrow '->' is used in two kinds of expressions, lambda (abstractions) and _switch_, necessary changes were made for the recognition of either (and further improvement touching lambda expressions will be separately arranged). Because 'default' is used for instance method declarations in interfaces and in _switch_ labels, necessary changes were made for the recognition of either (and further improvement touching method declarations will be separately arranged). Finally, it deemed appropriate to put 'yield' in the syntax group of javaOperator rather than javaStatement, for its member 'var' is also another contextual keyword (e.g., this is valid syntax: "var var = var(test.var);"). References: https://openjdk.org/jeps/361 (Switch Expressions) https://openjdk.org/jeps/440 (Record Patterns) https://openjdk.org/jeps/441 (Pattern Matching for switch) Also, add a Java specific filetype plugin for the syntax test, so that no soft-wrapping of long indented lines occur. Otherwise the syntax scripts would miss a few lines during scrolling and verification of the screen dumps. closes: #14105 Signed-off-by: Aliaksei Budavei <0x000c70@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Wed, 28 Feb 2024 21:45:09 +0100
parents e7749c0c978a
children a71e8f880dee
files runtime/syntax/java.vim runtime/syntax/testdir/dumps/java_switch_00.dump runtime/syntax/testdir/dumps/java_switch_01.dump runtime/syntax/testdir/dumps/java_switch_02.dump runtime/syntax/testdir/dumps/java_switch_03.dump runtime/syntax/testdir/dumps/java_switch_04.dump runtime/syntax/testdir/dumps/java_switch_05.dump runtime/syntax/testdir/dumps/java_switch_06.dump runtime/syntax/testdir/dumps/java_switch_99.dump runtime/syntax/testdir/ftplugin/java.vim runtime/syntax/testdir/input/java_switch.java
diffstat 11 files changed, 320 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/syntax/java.vim
+++ b/runtime/syntax/java.vim
@@ -2,7 +2,7 @@
 " Language:	Java
 " Maintainer:	Claudio Fleiner <claudio@fleiner.com>
 " URL:          https://github.com/fleiner/vim/blob/master/runtime/syntax/java.vim
-" Last Change:	2023 Aug 13
+" Last Change:	2024 Feb 27
 
 " Please check :help java.vim for comments on some of the options available.
 
@@ -37,9 +37,17 @@ syn keyword javaBoolean		true false
 syn keyword javaConstant	null
 syn keyword javaTypedef		this super
 syn keyword javaOperator	var new instanceof
+" Since the yield statement, which could take a parenthesised operand,
+" and _qualified_ yield methods get along within the switch block
+" (JLS-17, ยง3.8), it seems futile to make a region definition for this
+" block; instead look for the _yield_ word alone, and if found,
+" backtrack (arbitrarily) 80 bytes, at most, on the matched line and,
+" if necessary, on the line before that (h: \@<=), trying to match
+" neither a method reference nor a qualified method invocation.
+syn match   javaOperator	"\%(\%(::\|\.\)[[:space:]\n]*\)\@80<!\<yield\>"
 syn keyword javaType		boolean char byte short int long float double
 syn keyword javaType		void
-syn keyword javaStatement	return yield
+syn keyword javaStatement	return
 syn keyword javaStorageClass	static synchronized transient volatile final strictfp serializable
 syn keyword javaExceptions	throw try catch finally
 syn keyword javaAssert		assert
@@ -56,6 +64,7 @@ syn keyword javaBranch		break continue n
 syn match   javaUserLabelRef	"\k\+" contained
 syn match   javaVarArg		"\.\.\."
 syn keyword javaScopeDecl	public protected private abstract
+syn match   javaConceptKind	"\<default\>\%(\s*\%(:\|->\)\)\@!"
 
 function s:isModuleInfoDeclarationCurrentBuffer() abort
     return fnamemodify(bufname("%"), ":t") =~ '^module-info\%(\.class\>\)\@!'
@@ -142,15 +151,30 @@ if exists("java_space_errors")
   endif
 endif
 
-syn region  javaLabelRegion	transparent matchgroup=javaLabel start="\<case\>" matchgroup=NONE end=":\|->" contains=javaNumber,javaCharacter,javaString
-syn match   javaUserLabel	"^\s*[_$a-zA-Z][_$a-zA-Z0-9_]*\s*:"he=e-1 contains=javaLabel
-syn keyword javaLabel		default
+syn match   javaUserLabel	"^\s*\<\K\k*\>\%(\<default\>\)\@<!\s*:"he=e-1
+syn region  javaLabelRegion	transparent matchgroup=javaLabel start="\<case\>" matchgroup=NONE end=":\|->" contains=javaLabelCastType,javaLabelNumber,javaCharacter,javaString,javaConstant,@javaClasses,javaLabelDefault,javaLabelVarType,javaLabelWhenClause
+syn region  javaLabelRegion	transparent matchgroup=javaLabel start="\<default\>\%(\s*\%(:\|->\)\)\@=" matchgroup=NONE end=":\|->" oneline
+" Consider grouped _default_ _case_ labels, i.e.
+" case null, default ->
+" case null: default:
+syn keyword javaLabelDefault	contained default
+syn keyword javaLabelVarType	contained var
+syn keyword javaLabelCastType	contained char byte short int
+" Allow for the contingency of the enclosing region not being able to
+" _keep_ its _end_, e.g. case ':':.
+syn region  javaLabelWhenClause	contained transparent matchgroup=javaLabel start="\<when\>" matchgroup=NONE end=":"me=e-1 end="->"me=e-2 contains=TOP,javaExternal
+syn match   javaLabelNumber	contained "\<0\>[lL]\@!"
+syn match   javaLabelNumber	contained "\<\%(0\%([xX]\x\%(_*\x\)*\|_*\o\%(_*\o\)*\|[bB][01]\%(_*[01]\)*\)\|[1-9]\%(_*\d\)*\)\>[lL]\@!"
+hi def link javaLabelDefault	javaLabel
+hi def link javaLabelVarType	javaOperator
+hi def link javaLabelNumber	javaNumber
+hi def link javaLabelCastType	javaType
 
 " highlighting C++ keywords as errors removed, too many people find it
 " annoying.  Was: if !exists("java_allow_cpp_keywords")
 
 " The following cluster contains all java groups except the contained ones
-syn cluster javaTop add=javaExternal,javaError,javaBranch,javaLabelRegion,javaLabel,javaConditional,javaRepeat,javaBoolean,javaConstant,javaTypedef,javaOperator,javaType,javaStatement,javaStorageClass,javaAssert,javaExceptions,javaMethodDecl,javaClassDecl,javaScopeDecl,javaError2,javaUserLabel,javaLangObject,javaAnnotation,javaVarArg
+syn cluster javaTop add=javaExternal,javaError,javaBranch,javaLabelRegion,javaConditional,javaRepeat,javaBoolean,javaConstant,javaTypedef,javaOperator,javaType,javaStatement,javaStorageClass,javaAssert,javaExceptions,javaMethodDecl,javaClassDecl,javaScopeDecl,javaConceptKind,javaError2,javaUserLabel,javaLangObject,javaAnnotation,javaVarArg
 
 
 " Comments
@@ -231,9 +255,9 @@ if exists("java_highlight_functions")
     "	1. class names are always capitalized (ie: Button)
     "	2. method names are never capitalized (except constructors, of course)
     "syn region javaFuncDef start=+^\s\+\(\(public\|protected\|private\|static\|abstract\|final\|native\|synchronized\)\s\+\)*\(\(void\|boolean\|char\|byte\|short\|int\|long\|float\|double\|\([A-Za-z_][A-Za-z0-9_$]*\.\)*[A-Z][A-Za-z0-9_$]*\)\(<[^>]*>\)\=\(\[\]\)*\s\+[a-z][A-Za-z0-9_$]*\|[A-Z][A-Za-z0-9_$]*\)\s*([^0-9]+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,javaComment,javaLineComment,@javaClasses
-    syn region javaFuncDef start=+^\s\+\(\(public\|protected\|private\|static\|abstract\|final\|native\|synchronized\)\s\+\)*\(<.*>\s\+\)\?\(\(void\|boolean\|char\|byte\|short\|int\|long\|float\|double\|\([A-Za-z_][A-Za-z0-9_$]*\.\)*[A-Z][A-Za-z0-9_$]*\)\(<[^(){}]*>\)\=\(\[\]\)*\s\+[a-z][A-Za-z0-9_$]*\|[A-Z][A-Za-z0-9_$]*\)\s*(+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,javaComment,javaLineComment,@javaClasses,javaAnnotation
+    syn region javaFuncDef start=+^\s\+\%(\%(public\|protected\|private\|static\|\%(abstract\|default\)\|final\|native\|synchronized\)\s\+\)*\%(<.*>\s\+\)\?\%(\%(void\|boolean\|char\|byte\|short\|int\|long\|float\|double\|\%([A-Za-z_][A-Za-z0-9_$]*\.\)*[A-Z][A-Za-z0-9_$]*\)\%(<[^(){}]*>\)\=\%(\[\]\)*\s\+[a-z][A-Za-z0-9_$]*\|[A-Z][A-Za-z0-9_$]*\)\s*(+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,javaComment,javaLineComment,@javaClasses,javaAnnotation
   endif
-  syn match javaLambdaDef "[a-zA-Z_][a-zA-Z0-9_]*\s*->"
+  syn match   javaLambdaDef "\<\K\k*\>\%(\<default\>\)\@<!\s*->"
   syn match  javaBraces  "[{}]"
   syn cluster javaTop add=javaFuncDef,javaBraces,javaLambdaDef
 endif
@@ -326,6 +350,7 @@ hi def link javaStorageClass		StorageCla
 hi def link javaMethodDecl		javaStorageClass
 hi def link javaClassDecl		javaStorageClass
 hi def link javaScopeDecl		javaStorageClass
+hi def link javaConceptKind		NonText
 
 hi def link javaBoolean		Boolean
 hi def link javaSpecial		Special
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/java_switch_00.dump
@@ -0,0 +1,20 @@
+>c+0#00e0003#ffffff0|l|a|s@1| +0#0000000&|S|w|i|t|c|h|T|e|s|t|s| @2|/+0#0000e05&@1| |J|D|K| |2|1|+|.| +0#0000000&@43
+|{| @73
+@4|s+0#00e0003&|t|a|t|i|c| +0#0000000&|v+0#00e0003&|o|i|d| +0#0000000&|e|c|h|o|(|O|b|j|e|c|t| |o|)| |{| |S|y|s|t|e|m|.|o|u|t|.|p|r|i|n|t|l|n|(|o|)|;| |}| @17
+@75
+@4|s+0#00e0003&|t|a|t|i|c| +0#0000000&|{| @62
+@8|i+0#00e0003&|n|t|e|r|f|a|c|e| +0#0000000&|Y|i|e|l|d|a|b|l|e|<|T|>| @44
+@8|{| @65
+@12|T| |y+0#af5f00255&|i|e|l|d|(+0#0000000&|)|;| @52
+@12|d+0#4040ff13&|e|f|a|u|l|t| +0#0000000&|Y|i|e|l|d|a|b|l|e|<|T|>| |d|e|f|a|u|l|t|_|(|)| |{| |r+0#af5f00255&|e|t|u|r|n| +0#0000000&|t+0#00e0003&|h|i|s|;+0#0000000&| |}| @14
+@12|d+0#4040ff13&|e|f|a|u|l|t| +0#0000000&|Y|i|e|l|d|a|b|l|e|<|T|>| |w|h|e|n|(|)| |{| |r+0#af5f00255&|e|t|u|r|n| +0#0000000&|t+0#00e0003&|h|i|s|;+0#0000000&| |}| @18
+@8|}| @65
+@75
+@8|/+0#0000e05&@1| |T|h|e|r|e| |a|r|e| |8|0| |b|y|t|e|s| |(|\|@|8|0|<|!|)| |b|e|t|w|e@1|n| |"|:@1|"| |a|n|d| |"|y|i|e|l|d|;|"|.| +0#0000000&@9
+@8|Y|i|e|l|d|a|b|l|e|<|?|>| |y| |=| |(@1|Y|i|e|l|d|a|b|l|e|<|?|>|)| |(|)| |-|>| |0+0#e000002&|)+0#0000000&|:@1| @23
+@75
+@4|y|i|e|l|d|;| @64
+@8|(@1|Y|i|e|l|d|a|b|l|e|<|?|>|)| |(|)| |-|>| |0+0#e000002&|)+0#0000000&|.|w|h|e|n|(|)|.|d|e|f|a|u|l|t|_|(|)|.|y|i|e|l|d|(|)|;| @15
+@75
+@8|e+0#00e0003&|n|u|m| +0#0000000&|L|e|t@1|e|r|s| |{| |O|T|H|E|R|,| |A|L|P|H|A|,| |B|E|T|A| |}| @31
+@57|1|,|1| @10|T|o|p| 
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/java_switch_01.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@74
+@4|y|i|e|l|d|;| @64
+@8|(@1|Y|i|e|l|d|a|b|l|e|<|?|>|)| |(|)| |-|>| |0+0#e000002&|)+0#0000000&|.|w|h|e|n|(|)|.|d|e|f|a|u|l|t|_|(|)|.|y|i|e|l|d|(|)|;| @15
+@75
+@8|e+0#00e0003&|n|u|m| +0#0000000&|L|e|t@1|e|r|s| |{| |O|T|H|E|R|,| |A|L|P|H|A|,| |B|E|T|A| |}| @31
+> @74
+@8|L|e|t@1|e|r|s| |w|h|e|n| |=| |L|e|t@1|e|r|s|.|O|T|H|E|R|;| @37
+@75
+@8|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|w|h|e|n|)| |{| @51
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|A|L|P|H|A|:| |{| |e|c|h|o|(|L|e|t@1|e|r|s|.|A|L|P|H|A|)|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @23
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|B|E|T|A|:| @1|{| |e|c|h|o|(|L|e|t@1|e|r|s|.|B|E|T|A|)|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @24
+@8|d+0#af5f00255&|e|f|a|u|l|t|:+0#0000000&| @3|{| |e|c|h|o|(|L|e|t@1|e|r|s|.|O|T|H|E|R|)|;| |}| @30
+@8|}| @65
+@75
+@8|e|c|h|o|(|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|w|h|e|n|)| |{| @46
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|A|L|P|H|A| @1|-|>| |L|e|t@1|e|r|s|.|A|L|P|H|A|;| @33
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|B|E|T|A| @2|-|>| |L|e|t@1|e|r|s|.|B|E|T|A|;| @34
+@12|d+0#af5f00255&|e|f|a|u|l|t| +0#0000000&@4|-|>| |{| |y+0#af5f00255&|i|e|l|d|(+0#0000000&|L|e|t@1|e|r|s|.|O|T|H|E|R|)|;| |}| @22
+@8|}|)|;| @63
+@57|1|9|,|0|-|1| @7|1|3|%| 
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/java_switch_02.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@7|}|)|;| @63
+@75
+@8|S|t|r|i|n|g| |y+0#af5f00255&|i|e|l|d| +0#0000000&|=| |n+0#e000002&|u|l@1|;+0#0000000&| @46
+@75
+@8|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|y+0#af5f00255&|i|e|l|d|)+0#0000000&| |{| @50
+@8>c+0#af5f00255&|a|s|e| +0#0000000&|"+0#e000002&|A|"|:+0#0000000&| |c+0#af5f00255&|a|s|e| +0#0000000&|"+0#e000002&|B|"|:+0#0000000&| |{| |e|c|h|o|(|"+0#e000002&|A| |o|r| |B|"|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @20
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#e000002&|:|"|:+0#0000000&| @6|{| |e|c|h|o|(|"+0#e000002&|C|o|l|o|n|"|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @25
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|S|t|r|i|n|g| |s|t|r| |w+0#af5f00255&|h|e|n| +0#0000000&|!|s|t|r|.|e|q|u|a|l|s|(|"+0#e000002&@1|)+0#0000000&|:| @29
+@20|{| |e|c|h|o|(|"+0#e000002&|<|n|o|n|-|e|m|p|t|y|>|"|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @23
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|n+0#e000002&|u|l@1|:+0#0000000&| |d+0#af5f00255&|e|f|a|u|l|t|:+0#0000000&| |{| |e|c|h|o|(|"+0#e000002&|O|t|h|e|r|"|)+0#0000000&|;| |}| @28
+@8|}| @65
+@75
+@8|e|c|h|o|(|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|y+0#af5f00255&|i|e|l|d|)+0#0000000&| |{| @45
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#e000002&|A|"|,+0#0000000&| |"+0#e000002&|B|"| +0#0000000&@6|-|>| |{| |y+0#af5f00255&|i|e|l|d|(+0#0000000&|"+0#e000002&|A| |o|r| |B|"|)+0#0000000&|;| |}| @19
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#e000002&|-|>|"| +0#0000000&@6|-|>| |"+0#e000002&|A|r@1|o|w|"|;+0#0000000&| @35
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|S|t|r|i|n|g| |s|t|r| |w+0#af5f00255&|h|e|n| +0#0000000&|!|s|t|r|.|e|q|u|a|l|s|(|"+0#e000002&@1|)+0#0000000&| @26
+@24|-|>| |"+0#e000002&|<|n|o|n|-|e|m|p|t|y|>|"|;+0#0000000&| @33
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|n+0#e000002&|u|l@1|,+0#0000000&| |d+0#af5f00255&|e|f|a|u|l|t| +0#0000000&@1|-|>| |"+0#e000002&|O|t|h|e|r|"|;+0#0000000&| @31
+@8|}|)|;| @63
+@57|3|7|,|3|-|9| @7|3|0|%| 
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/java_switch_03.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@7|}|)|;| @63
+@75
+@8|O|b|j|e|c|t| |o| |=| |n+0#af5f00255&|e|w| +0#0000000&|O|b|j|e|c|t|(|)|;| @42
+@75
+@8|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|o|)| |{| @54
+@8>c+0#af5f00255&|a|s|e| +0#0000000&|n+0#e000002&|u|l@1|:+0#0000000&| @5|{| |e|c|h|o|(|"+0#e000002&|n|u|l@1|"|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @26
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|L|e|t@1|e|r|s|[|]| |l@1|:| @1|{| |e|c|h|o|(|"+0#e000002&|S|w|i|t|c|h|T|e|s|t|s|$|1|L|e|t@1|e|r|s|[|]|"|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @4
+@8|d+0#af5f00255&|e|f|a|u|l|t|:+0#0000000&| @7|{| |e|c|h|o|(|"+0#e000002&|j|a|v|a|.|l|a|n|g|.|O|b|j|e|c|t|"|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @14
+@8|}|;| @64
+@75
+@8|e|c|h|o|(|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|o|)| |{| @49
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|n+0#e000002&|u|l@1| +0#0000000&@6|-|>| |"+0#e000002&|n|u|l@1|"|;+0#0000000&| @36
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|L|e|t@1|e|r|s|[|]| |l@1| @2|-|>| |"+0#e000002&|S|w|i|t|c|h|T|e|s|t|s|$|1|L|e|t@1|e|r|s|[|]|"|;+0#0000000&| @14
+@12|d+0#af5f00255&|e|f|a|u|l|t| +0#0000000&@8|-|>| |"+0#e000002&|j|a|v|a|.|l|a|n|g|.|O|b|j|e|c|t|"|;+0#0000000&| @24
+@8|}|)|;| @63
+@75
+@8|c+0#00e0003&|h|a|r| +0#0000000&|c|h| |=| |'+0#e000002&|c|'|;+0#0000000&| @52
+@75
+@8|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|c|h|)| |{| @53
+@57|5@1|,|3|-|9| @7|4|7|%| 
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/java_switch_04.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@7|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|c|h|)| |{| @53
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|'+0#e000002&|a|'|:+0#0000000&| @6|{| |e|c|h|o|(|'+0#e000002&|a|'|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @29
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|'+0#e000002&|b|'|:+0#0000000&| @6|{| |e|c|h|o|(|'+0#e000002&|b|'|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @29
+@8|d+0#af5f00255&|e|f|a|u|l|t|:+0#0000000&| @7|{| |e|c|h|o|(|'+0#e000002&|\+0#e000e06&|u|0@3|'+0#e000002&|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @24
+@8|}|;| @64
+> @74
+@8|e|c|h|o|(|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|c|h|)| |{| @48
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|'+0#e000002&|a|'| +0#0000000&@3|-|>| |'+0#e000002&|a|'|;+0#0000000&| @43
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|'+0#e000002&|b|'| +0#0000000&@3|-|>| |'+0#e000002&|b|'|;+0#0000000&| @43
+@12|d+0#af5f00255&|e|f|a|u|l|t| +0#0000000&@4|-|>| |'+0#e000002&|\+0#e000e06&|u|0@3|'+0#e000002&|;+0#0000000&| @38
+@8|}|)|;| @63
+@75
+@8|b+0#00e0003&|y|t|e| +0#0000000&|b| |=| |(|b+0#00e0003&|y|t|e|)+0#0000000&| |2+0#e000002&|;+0#0000000&| @48
+@75
+@8|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|b|)| |{| @54
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|(@1|b+0#00e0003&|y|t|e|)+0#0000000&| |0+0#e000002&|)+0#0000000&|:| @3|{| |e|c|h|o|(@1|b+0#00e0003&|y|t|e|)+0#0000000&| |0+0#e000002&|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @20
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|(@1|b+0#00e0003&|y|t|e|)+0#0000000&| |1+0#e000002&|)+0#0000000&|:| @3|{| |e|c|h|o|(@1|b+0#00e0003&|y|t|e|)+0#0000000&| |1+0#e000002&|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @20
+@8|d+0#af5f00255&|e|f|a|u|l|t|:+0#0000000&| @7|{| |e|c|h|o|(@1|b+0#00e0003&|y|t|e|)+0#0000000&| |-|1+0#e000002&|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @23
+@8|}|;| @64
+@57|7|3|,|0|-|1| @7|6|5|%| 
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/java_switch_05.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@7|}|;| @64
+@75
+@8|e|c|h|o|(|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|b|)| |{| @49
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|(@1|b+0#00e0003&|y|t|e|)+0#0000000&| |0+0#e000002&|)+0#0000000&| |-|>| |(|b+0#00e0003&|y|t|e|)+0#0000000&| |0+0#e000002&|;+0#0000000&| @34
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|(@1|b+0#00e0003&|y|t|e|)+0#0000000&| |1+0#e000002&|)+0#0000000&| |-|>| |(|b+0#00e0003&|y|t|e|)+0#0000000&| |1+0#e000002&|;+0#0000000&| @34
+@12>d+0#af5f00255&|e|f|a|u|l|t| +0#0000000&@4|-|>| |(|b+0#00e0003&|y|t|e|)+0#0000000&| |-|1+0#e000002&|;+0#0000000&| @37
+@8|}|)|;| @63
+@75
+@8|s+0#00e0003&|h|o|r|t| +0#0000000&|s|h| |=| |(|s+0#00e0003&|h|o|r|t|)+0#0000000&| |2+0#e000002&|;+0#0000000&| @45
+@75
+@8|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|s|h|)| |{| @53
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|(@1|s+0#00e0003&|h|o|r|t|)+0#0000000&| |0+0#e000002&|)+0#0000000&|:| @2|{| |e|c|h|o|(@1|s+0#00e0003&|h|o|r|t|)+0#0000000&| |0+0#e000002&|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @19
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|(@1|s+0#00e0003&|h|o|r|t|)+0#0000000&| |1+0#e000002&|)+0#0000000&|:| @2|{| |e|c|h|o|(@1|s+0#00e0003&|h|o|r|t|)+0#0000000&| |1+0#e000002&|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @19
+@8|d+0#af5f00255&|e|f|a|u|l|t|:+0#0000000&| @7|{| |e|c|h|o|(@1|s+0#00e0003&|h|o|r|t|)+0#0000000&| |-|1+0#e000002&|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @22
+@8|}|;| @64
+@75
+@8|e|c|h|o|(|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|s|h|)| |{| @48
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|(@1|s+0#00e0003&|h|o|r|t|)+0#0000000&| |0+0#e000002&|)+0#0000000&| @3|-|>| |(|s+0#00e0003&|h|o|r|t|)+0#0000000&| |0+0#e000002&|;+0#0000000&| @29
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|(@1|s+0#00e0003&|h|o|r|t|)+0#0000000&| |1+0#e000002&|)+0#0000000&| @3|-|>| |(|s+0#00e0003&|h|o|r|t|)+0#0000000&| |1+0#e000002&|;+0#0000000&| @29
+@57|9|1|,|4|-|1|3| @6|8|2|%| 
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/java_switch_06.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@11|c+0#af5f00255&|a|s|e| +0#0000000&|(@1|s+0#00e0003&|h|o|r|t|)+0#0000000&| |1+0#e000002&|)+0#0000000&| @3|-|>| |(|s+0#00e0003&|h|o|r|t|)+0#0000000&| |1+0#e000002&|;+0#0000000&| @29
+@12|d+0#af5f00255&|e|f|a|u|l|t| +0#0000000&@8|-|>| |(|s+0#00e0003&|h|o|r|t|)+0#0000000&| |-|1+0#e000002&|;+0#0000000&| @32
+@8|}|)|;| @63
+@75
+@8|i+0#00e0003&|n|t| +0#0000000&|i| |=| |2+0#e000002&|;+0#0000000&| @56
+> @74
+@8|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|i|)| |{| @54
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|0+0#e000002&|b|0|_@1|0@1|_@2|0@2|:+0#0000000&| |{| |e|c|h|o|(|0+0#e000002&|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @27
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|0+0#e000002&|x|0@2|_@2|0@1|_@1|1|:+0#0000000&| |{| |e|c|h|o|(|1+0#e000002&|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @27
+@8|d+0#af5f00255&|e|f|a|u|l|t|:+0#0000000&| @7|{| |e|c|h|o|(|-|1+0#e000002&|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @30
+@8|}|;| @64
+@75
+@8|e|c|h|o|(|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|i|)| |{| @49
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|0+0#e000002&|_|0|_|0|_|0|_|0| +0#0000000&@1|-|>| |0+0#e000002&|;+0#0000000&| @41
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|1+0#e000002&| +0#0000000&@5|-|>| |1+0#e000002&|;+0#0000000&| @45
+@12|d+0#af5f00255&|e|f|a|u|l|t| +0#0000000&@4|-|>| |-|1+0#e000002&|;+0#0000000&| @44
+@8|}|)|;| @63
+@4|}| @69
+|}| @73
+@57|1|0|9|,|0|-|1| @6|B|o|t| 
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/java_switch_99.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@11|c+0#af5f00255&|a|s|e| +0#0000000&|(@1|s+0#00e0003&|h|o|r|t|)+0#0000000&| |1+0#e000002&|)+0#0000000&| @3|-|>| |(|s+0#00e0003&|h|o|r|t|)+0#0000000&| |1+0#e000002&|;+0#0000000&| @29
+@12|d+0#af5f00255&|e|f|a|u|l|t| +0#0000000&@8|-|>| |(|s+0#00e0003&|h|o|r|t|)+0#0000000&| |-|1+0#e000002&|;+0#0000000&| @32
+@8|}|)|;| @63
+@75
+@8|i+0#00e0003&|n|t| +0#0000000&|i| |=| |2+0#e000002&|;+0#0000000&| @56
+@75
+@8|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|i|)| |{| @54
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|0+0#e000002&|b|0|_@1|0@1|_@2|0@2|:+0#0000000&| |{| |e|c|h|o|(|0+0#e000002&|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @27
+@8|c+0#af5f00255&|a|s|e| +0#0000000&|0+0#e000002&|x|0@2|_@2|0@1|_@1|1|:+0#0000000&| |{| |e|c|h|o|(|1+0#e000002&|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @27
+@8|d+0#af5f00255&|e|f|a|u|l|t|:+0#0000000&| @7|{| |e|c|h|o|(|-|1+0#e000002&|)+0#0000000&|;| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| |}| @30
+@8|}|;| @64
+@75
+@8|e|c|h|o|(|s+0#af5f00255&|w|i|t|c|h| +0#0000000&|(|i|)| |{| @49
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|0+0#e000002&|_|0|_|0|_|0|_|0| +0#0000000&@1|-|>| |0+0#e000002&|;+0#0000000&| @41
+@12|c+0#af5f00255&|a|s|e| +0#0000000&|1+0#e000002&| +0#0000000&@5|-|>| |1+0#e000002&|;+0#0000000&| @45
+@12|d+0#af5f00255&|e|f|a|u|l|t| +0#0000000&@4|-|>| |-|1+0#e000002&|;+0#0000000&| @44
+@8|}|)|;| @63
+@4|}| @69
+>}| @73
+@57|1|2@1|,|1| @8|B|o|t| 
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/ftplugin/java.vim
@@ -0,0 +1,5 @@
+" The Java source file uses long lines with tabs
+" so use a smaller tabstop value, so that no
+" soft-wrapping occurs (which may cause the syntax
+" tests to miss some lines during scrolling)
+setl ts=4
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/input/java_switch.java
@@ -0,0 +1,122 @@
+class SwitchTests	// JDK 21+.
+{
+	static void echo(Object o) { System.out.println(o); }
+
+	static {
+		interface Yieldable<T>
+		{
+			T yield();
+			default Yieldable<T> default_()	{ return this; }
+			default Yieldable<T> when()	{ return this; }
+		}
+
+		// There are 80 bytes (\@80<!) between "::" and "yield;".
+		Yieldable<?> y = ((Yieldable<?>) () -> 0)::
+                                                                               yield;
+		((Yieldable<?>) () -> 0).when().default_().yield();
+
+		enum Letters { OTHER, ALPHA, BETA }
+
+		Letters when = Letters.OTHER;
+
+		switch (when) {
+		case ALPHA:	{ echo(Letters.ALPHA); break; }
+		case BETA:	{ echo(Letters.BETA); break; }
+		default:	{ echo(Letters.OTHER); }
+		}
+
+		echo(switch (when) {
+			case ALPHA	-> Letters.ALPHA;
+			case BETA	-> Letters.BETA;
+			default		-> { yield(Letters.OTHER); }
+		});
+
+		String yield = null;
+
+		switch (yield) {
+		case "A": case "B":	{ echo("A or B"); break; }
+		case ":":		{ echo("Colon"); break; }
+		case String str when !str.equals(""):
+					{ echo("<non-empty>"); break; }
+		case null: default:	{ echo("Other"); }
+		}
+
+		echo(switch (yield) {
+			case "A", "B"		-> { yield("A or B"); }
+			case "->"		-> "Arrow";
+			case String str when !str.equals("")
+						-> "<non-empty>";
+			case null, default	-> "Other";
+		});
+
+		Object o = new Object();
+
+		switch (o) {
+		case null:		{ echo("null"); break; }
+		case Letters[] ll:	{ echo("SwitchTests$1Letters[]"); break; }
+		default:		{ echo("java.lang.Object"); break; }
+		};
+
+		echo(switch (o) {
+			case null		-> "null";
+			case Letters[] ll	-> "SwitchTests$1Letters[]";
+			default			-> "java.lang.Object";
+		});
+
+		char ch = 'c';
+
+		switch (ch) {
+		case 'a':		{ echo('a'); break; }
+		case 'b':		{ echo('b'); break; }
+		default:		{ echo('\u0000'); break; }
+		};
+
+		echo(switch (ch) {
+			case 'a'	-> 'a';
+			case 'b'	-> 'b';
+			default		-> '\u0000';
+		});
+
+		byte b = (byte) 2;
+
+		switch (b) {
+		case ((byte) 0):	{ echo((byte) 0); break; }
+		case ((byte) 1):	{ echo((byte) 1); break; }
+		default:		{ echo((byte) -1); break; }
+		};
+
+		echo(switch (b) {
+			case ((byte) 0)	-> (byte) 0;
+			case ((byte) 1)	-> (byte) 1;
+			default		-> (byte) -1;
+		});
+
+		short sh = (short) 2;
+
+		switch (sh) {
+		case ((short) 0):	{ echo((short) 0); break; }
+		case ((short) 1):	{ echo((short) 1); break; }
+		default:		{ echo((short) -1); break; }
+		};
+
+		echo(switch (sh) {
+			case ((short) 0)	-> (short) 0;
+			case ((short) 1)	-> (short) 1;
+			default			-> (short) -1;
+		});
+
+		int i = 2;
+
+		switch (i) {
+		case 0b0__00___000:	{ echo(0); break; }
+		case 0x000___00__1:	{ echo(1); break; }
+		default:		{ echo(-1); break; }
+		};
+
+		echo(switch (i) {
+			case 0_0_0_0_0	-> 0;
+			case 1		-> 1;
+			default		-> -1;
+		});
+	}
+}