diff --git a/lexers/k/kotlin.go b/lexers/k/kotlin.go index 3e90af7d3..17cdfa629 100644 --- a/lexers/k/kotlin.go +++ b/lexers/k/kotlin.go @@ -28,29 +28,67 @@ var Kotlin = internal.Register(MustNewLexer( {`%=|&&|\*=|\+\+|\+=|--|-=|->|\.\.|\/=|::|<=|==|>=|!!|!=|\|\||\?[:.]`, Operator, nil}, {`[~!%^&*()+=|\[\]:;,.<>\/?-]`, Punctuation, nil}, {`[{}]`, Punctuation, nil}, - {`"""[^"]*"""`, LiteralString, nil}, - {`"(\\\\|\\"|[^"\n])*["\n]`, LiteralString, nil}, + {`"""`, LiteralString, Push("rawstring")}, + {`"`, LiteralStringDouble, Push("string")}, + {`(')(\\u[0-9a-fA-F]{4})(')`, ByGroups(LiteralStringChar, LiteralStringEscape, LiteralStringChar), nil}, {`'\\.'|'[^\\]'`, LiteralStringChar, nil}, {`0[xX][0-9a-fA-F]+[Uu]?[Ll]?|[0-9]+(\.[0-9]*)?([eE][+-][0-9]+)?[fF]?[Uu]?[Ll]?`, LiteralNumber, nil}, {`(companion)(\s+)(object)`, ByGroups(Keyword, Text, Keyword), nil}, {`(class|interface|object)(\s+)`, ByGroups(Keyword, Text), Push("class")}, {`(package|import)(\s+)`, ByGroups(Keyword, Text), Push("package")}, {`(val|var)(\s+)`, ByGroups(Keyword, Text), Push("property")}, - {`(fun)(\s+)(<[^>]*>\s+)?`, ByGroups(Keyword, Text, Text), Push("function")}, + {`(fun)(\s+)`, ByGroups(Keyword, Text), Push("function")}, {`(abstract|actual|annotation|as|as\?|break|by|catch|class|companion|const|constructor|continue|crossinline|data|delegate|do|dynamic|else|enum|expect|external|false|field|file|final|finally|for|fun|get|if|import|in|infix|init|inline|inner|interface|internal|is|it|lateinit|noinline|null|object|open|operator|out|override|package|param|private|property|protected|public|receiver|reified|return|sealed|set|setparam|super|suspend|tailrec|this|throw|true|try|typealias|typeof|val|var|vararg|when|where|while)\b`, Keyword, nil}, - {"(@?[" + kotlinIdentifier + "]*`)", Name, nil}, + {`@[` + kotlinIdentifier + `]+`, NameDecorator, nil}, + {`[` + kotlinIdentifier + `]+`, Name, nil}, }, "package": { {`\S+`, NameNamespace, Pop(1)}, }, "class": { - {"(@?[" + kotlinIdentifier + "]*`)", NameClass, Pop(1)}, + // \x60 is the back tick character (`) + {`\x60[^\x60]+?\x60`, NameClass, Pop(1)}, + {`[` + kotlinIdentifier + `]+`, NameClass, Pop(1)}, }, "property": { - {"(@?[" + kotlinIdentifier + " ]*`)", NameProperty, Pop(1)}, + {`\x60[^\x60]+?\x60`, NameProperty, Pop(1)}, + {`[` + kotlinIdentifier + `]+`, NameProperty, Pop(1)}, + }, + "generics-specification": { + {`<`, Punctuation, Push("generics-specification")}, // required for generics inside generics e.g. > + {`>`, Punctuation, Pop(1)}, + {`[,:*?]`, Punctuation, nil}, + {`(in|out|reified)`, Keyword, nil}, + {`\x60[^\x60]+?\x60`, NameClass, nil}, + {`[` + kotlinIdentifier + `]+`, NameClass, nil}, + {`\s+`, Text, nil}, }, "function": { - {"(@?[" + kotlinIdentifier + " ]*`)", NameFunction, Pop(1)}, + {`<`, Punctuation, Push("generics-specification")}, + {`\x60[^\x60]+?\x60`, NameFunction, Pop(1)}, + {`[` + kotlinIdentifier + `]+`, NameFunction, Pop(1)}, + {`\s+`, Text, nil}, + }, + "rawstring": { + // raw strings don't allow character escaping + {`"""`, LiteralString, Pop(1)}, + {`(?:[^$"]+|\"{1,2}[^"])+`, LiteralString, nil}, + Include("string-interpol"), + // remaining dollar signs are just a string + {`\$`, LiteralString, nil}, + }, + "string": { + {`\\[tbnr'"\\\$]`, LiteralStringEscape, nil}, + {`\\u[0-9a-fA-F]{4}`, LiteralStringEscape, nil}, + {`"`, LiteralStringDouble, Pop(1)}, + Include("string-interpol"), + {`[^\n\\"$]+`, LiteralStringDouble, nil}, + // remaining dollar signs are just a string + {`\$`, LiteralStringDouble, nil}, + }, + "string-interpol": { + {`\$[` + kotlinIdentifier + `]+`, LiteralStringInterpol, nil}, + {`\${[^}\n]*}`, LiteralStringInterpol, nil}, }, }, )) diff --git a/lexers/testdata/kotlin.actual b/lexers/testdata/kotlin.actual index e28a03ab6..68e05af4c 100644 --- a/lexers/testdata/kotlin.actual +++ b/lexers/testdata/kotlin.actual @@ -21,6 +21,23 @@ fun nullable2(nullable: String?): Int = nullable?.length ?: run { 1 + 2 } +val rawStringWithQuotes = """ +Hello "example" ${1 + 2} +And now { Just the braces } +Escapes here don't work so this is just text \t \n \u1234 $ \$ +""" + +fun returnsSomething(): Int { + return "".length +} + +fun returnsSomethingElse(): Int = "\\\n".length + +val character = '"' +val escapedCharacter = '\"' +// escaping a double quote character inside a character is optional +val stringWithSingleQuote = "'" + typealias MySecretAlias = (A, B) -> Unit val impl : MySecretAlias = { a, _ -> Unit } @@ -66,6 +83,42 @@ fun moreOperators(arg: Any?) { println(arg === Unit) } +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, + AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION) +@Retention(AnnotationRetention.SOURCE) +annotation class Annotated + +@Annotated class A { + @Annotated fun a( + @Annotated param: Int + ) { + + @Annotated val y = 0 + + } +} + +open class TUpper +fun generic() = 123 + +class Holder + +class Holder2 + +var holder1: Holder? = null + +class Some( + val holder2: Holder? = null +) { + var holder3: Holder? = null + fun example() { + var holder4: Holder? = null + } +} +fun > sort(list: List) { + +} + class X { companion object { } diff --git a/lexers/testdata/kotlin.expected b/lexers/testdata/kotlin.expected index f562eee9e..bf1caaf9a 100644 --- a/lexers/testdata/kotlin.expected +++ b/lexers/testdata/kotlin.expected @@ -27,14 +27,12 @@ {"type":"Text","value":"\n "}, {"type":"Keyword","value":"val"}, {"type":"Text","value":" "}, - {"type":"Error","value":"` + \""}, - {"type":"NameProperty","value":"with"}, - {"type":"Text","value":" "}, - {"type":"Name","value":"spaces"}, - {"type":"LiteralString","value":"\" + ` = \""}, - {"type":"Name","value":"hello"}, - {"type":"LiteralString","value":"\"\n"}, - {"type":"Text","value":" "}, + {"type":"NameProperty","value":"` + \"with spaces\" + `"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"="}, + {"type":"Text","value":" "}, + {"type":"LiteralStringDouble","value":"\"hello\""}, + {"type":"Text","value":"\n "}, {"type":"Keyword","value":"val"}, {"type":"Text","value":" "}, {"type":"NameProperty","value":"multiline"}, @@ -130,6 +128,73 @@ {"type":"Text","value":"\n"}, {"type":"Punctuation","value":"}"}, {"type":"Text","value":"\n\n"}, + {"type":"Keyword","value":"val"}, + {"type":"Text","value":" "}, + {"type":"NameProperty","value":"rawStringWithQuotes"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"="}, + {"type":"Text","value":" "}, + {"type":"LiteralString","value":"\"\"\"\nHello \"example\" "}, + {"type":"LiteralStringInterpol","value":"${1 + 2}"}, + {"type":"LiteralString","value":"\nAnd now { Just the braces }\nEscapes here don't work so this is just text \\t \\n \\u1234 $ \\$\n\"\"\""}, + {"type":"Text","value":"\n\n"}, + {"type":"Keyword","value":"fun"}, + {"type":"Text","value":" "}, + {"type":"NameFunction","value":"returnsSomething"}, + {"type":"Punctuation","value":"():"}, + {"type":"Text","value":" "}, + {"type":"Name","value":"Int"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"{"}, + {"type":"Text","value":"\n "}, + {"type":"Keyword","value":"return"}, + {"type":"Text","value":" "}, + {"type":"LiteralStringDouble","value":"\"\""}, + {"type":"Punctuation","value":"."}, + {"type":"Name","value":"length"}, + {"type":"Text","value":"\n"}, + {"type":"Punctuation","value":"}"}, + {"type":"Text","value":"\n\n"}, + {"type":"Keyword","value":"fun"}, + {"type":"Text","value":" "}, + {"type":"NameFunction","value":"returnsSomethingElse"}, + {"type":"Punctuation","value":"():"}, + {"type":"Text","value":" "}, + {"type":"Name","value":"Int"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"="}, + {"type":"Text","value":" "}, + {"type":"LiteralStringDouble","value":"\""}, + {"type":"LiteralStringEscape","value":"\\\\\\n"}, + {"type":"LiteralStringDouble","value":"\""}, + {"type":"Punctuation","value":"."}, + {"type":"Name","value":"length"}, + {"type":"Text","value":"\n\n"}, + {"type":"Keyword","value":"val"}, + {"type":"Text","value":" "}, + {"type":"NameProperty","value":"character"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"="}, + {"type":"Text","value":" "}, + {"type":"LiteralStringChar","value":"'\"'"}, + {"type":"Text","value":"\n"}, + {"type":"Keyword","value":"val"}, + {"type":"Text","value":" "}, + {"type":"NameProperty","value":"escapedCharacter"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"="}, + {"type":"Text","value":" "}, + {"type":"LiteralStringChar","value":"'\\\"'"}, + {"type":"Text","value":"\n"}, + {"type":"CommentSingle","value":"// escaping a double quote character inside a character is optional\n"}, + {"type":"Keyword","value":"val"}, + {"type":"Text","value":" "}, + {"type":"NameProperty","value":"stringWithSingleQuote"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"="}, + {"type":"Text","value":" "}, + {"type":"LiteralStringDouble","value":"\"'\""}, + {"type":"Text","value":"\n\n"}, {"type":"Keyword","value":"typealias"}, {"type":"Text","value":" "}, {"type":"Name","value":"MySecretAlias"}, @@ -214,7 +279,13 @@ {"type":"Text","value":"\n "}, {"type":"Name","value":"println"}, {"type":"Punctuation","value":"("}, - {"type":"LiteralString","value":"\"This is an example a = $a and the sum is ${a + b} ${ A.foo() }\""}, + {"type":"LiteralStringDouble","value":"\"This is an example a = "}, + {"type":"LiteralStringInterpol","value":"$a"}, + {"type":"LiteralStringDouble","value":" and the sum is "}, + {"type":"LiteralStringInterpol","value":"${a + b}"}, + {"type":"LiteralStringDouble","value":" "}, + {"type":"LiteralStringInterpol","value":"${ A.foo() }"}, + {"type":"LiteralStringDouble","value":"\""}, {"type":"Punctuation","value":")"}, {"type":"Text","value":"\n "}, {"type":"Name","value":"println"}, @@ -415,6 +486,260 @@ {"type":"Text","value":"\n"}, {"type":"Punctuation","value":"}"}, {"type":"Text","value":"\n\n"}, + {"type":"NameDecorator","value":"@Target"}, + {"type":"Punctuation","value":"("}, + {"type":"Name","value":"AnnotationTarget"}, + {"type":"Punctuation","value":"."}, + {"type":"Name","value":"CLASS"}, + {"type":"Punctuation","value":","}, + {"type":"Text","value":" "}, + {"type":"Name","value":"AnnotationTarget"}, + {"type":"Punctuation","value":"."}, + {"type":"Name","value":"FUNCTION"}, + {"type":"Punctuation","value":","}, + {"type":"Text","value":"\n "}, + {"type":"Name","value":"AnnotationTarget"}, + {"type":"Punctuation","value":"."}, + {"type":"Name","value":"VALUE_PARAMETER"}, + {"type":"Punctuation","value":","}, + {"type":"Text","value":" "}, + {"type":"Name","value":"AnnotationTarget"}, + {"type":"Punctuation","value":"."}, + {"type":"Name","value":"EXPRESSION"}, + {"type":"Punctuation","value":")"}, + {"type":"Text","value":"\n"}, + {"type":"NameDecorator","value":"@Retention"}, + {"type":"Punctuation","value":"("}, + {"type":"Name","value":"AnnotationRetention"}, + {"type":"Punctuation","value":"."}, + {"type":"Name","value":"SOURCE"}, + {"type":"Punctuation","value":")"}, + {"type":"Text","value":"\n"}, + {"type":"Keyword","value":"annotation"}, + {"type":"Text","value":" "}, + {"type":"Keyword","value":"class"}, + {"type":"Text","value":" "}, + {"type":"NameClass","value":"Annotated"}, + {"type":"Text","value":"\n\n"}, + {"type":"NameDecorator","value":"@Annotated"}, + {"type":"Text","value":" "}, + {"type":"Keyword","value":"class"}, + {"type":"Text","value":" "}, + {"type":"NameClass","value":"A"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"{"}, + {"type":"Text","value":"\n "}, + {"type":"NameDecorator","value":"@Annotated"}, + {"type":"Text","value":" "}, + {"type":"Keyword","value":"fun"}, + {"type":"Text","value":" "}, + {"type":"NameFunction","value":"a"}, + {"type":"Punctuation","value":"("}, + {"type":"Text","value":"\n "}, + {"type":"NameDecorator","value":"@Annotated"}, + {"type":"Text","value":" "}, + {"type":"Keyword","value":"param"}, + {"type":"Punctuation","value":":"}, + {"type":"Text","value":" "}, + {"type":"Name","value":"Int"}, + {"type":"Text","value":"\n "}, + {"type":"Punctuation","value":")"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"{"}, + {"type":"Text","value":"\n\n "}, + {"type":"NameDecorator","value":"@Annotated"}, + {"type":"Text","value":" "}, + {"type":"Keyword","value":"val"}, + {"type":"Text","value":" "}, + {"type":"NameProperty","value":"y"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"="}, + {"type":"Text","value":" "}, + {"type":"LiteralNumber","value":"0"}, + {"type":"Text","value":"\n\n "}, + {"type":"Punctuation","value":"}"}, + {"type":"Text","value":"\n"}, + {"type":"Punctuation","value":"}"}, + {"type":"Text","value":"\n\n"}, + {"type":"Keyword","value":"open"}, + {"type":"Text","value":" "}, + {"type":"Keyword","value":"class"}, + {"type":"Text","value":" "}, + {"type":"NameClass","value":"TUpper"}, + {"type":"Text","value":"\n"}, + {"type":"Keyword","value":"fun"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"\u003c"}, + {"type":"NameClass","value":"T"}, + {"type":"Punctuation","value":":"}, + {"type":"Text","value":" "}, + {"type":"NameClass","value":"TUpper"}, + {"type":"Punctuation","value":","}, + {"type":"Text","value":" "}, + {"type":"NameClass","value":"R"}, + {"type":"Punctuation","value":":"}, + {"type":"Text","value":" "}, + {"type":"NameClass","value":"Any"}, + {"type":"Punctuation","value":"?\u003e"}, + {"type":"Text","value":" "}, + {"type":"NameFunction","value":"generic"}, + {"type":"Punctuation","value":"()"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"="}, + {"type":"Text","value":" "}, + {"type":"LiteralNumber","value":"123"}, + {"type":"Text","value":"\n\n"}, + {"type":"Keyword","value":"class"}, + {"type":"Text","value":" "}, + {"type":"NameClass","value":"Holder"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"\u003c"}, + {"type":"Keyword","value":"in"}, + {"type":"Text","value":" "}, + {"type":"Name","value":"A"}, + {"type":"Punctuation","value":","}, + {"type":"Text","value":" "}, + {"type":"Keyword","value":"out"}, + {"type":"Text","value":" "}, + {"type":"Name","value":"B"}, + {"type":"Punctuation","value":","}, + {"type":"Text","value":" "}, + {"type":"Name","value":"C"}, + {"type":"Punctuation","value":":"}, + {"type":"Text","value":" "}, + {"type":"Name","value":"TUpper"}, + {"type":"Punctuation","value":"\u003e"}, + {"type":"Text","value":"\n\n"}, + {"type":"Keyword","value":"class"}, + {"type":"Text","value":" "}, + {"type":"NameClass","value":"Holder2"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"\u003c"}, + {"type":"Name","value":"T"}, + {"type":"Punctuation","value":"\u003e"}, + {"type":"Text","value":"\n\n"}, + {"type":"Keyword","value":"var"}, + {"type":"Text","value":" "}, + {"type":"NameProperty","value":"holder1"}, + {"type":"Punctuation","value":":"}, + {"type":"Text","value":" "}, + {"type":"Name","value":"Holder"}, + {"type":"Punctuation","value":"\u003c"}, + {"type":"Name","value":"Int"}, + {"type":"Punctuation","value":","}, + {"type":"Text","value":" "}, + {"type":"Name","value":"String"}, + {"type":"Punctuation","value":","}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"*\u003e?"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"="}, + {"type":"Text","value":" "}, + {"type":"Keyword","value":"null"}, + {"type":"Text","value":"\n\n"}, + {"type":"Keyword","value":"class"}, + {"type":"Text","value":" "}, + {"type":"NameClass","value":"Some"}, + {"type":"Punctuation","value":"("}, + {"type":"Text","value":"\n "}, + {"type":"Keyword","value":"val"}, + {"type":"Text","value":" "}, + {"type":"NameProperty","value":"holder2"}, + {"type":"Punctuation","value":":"}, + {"type":"Text","value":" "}, + {"type":"Name","value":"Holder"}, + {"type":"Punctuation","value":"\u003c"}, + {"type":"Name","value":"Int"}, + {"type":"Punctuation","value":","}, + {"type":"Text","value":" "}, + {"type":"Name","value":"String"}, + {"type":"Punctuation","value":","}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"*\u003e?"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"="}, + {"type":"Text","value":" "}, + {"type":"Keyword","value":"null"}, + {"type":"Text","value":"\n"}, + {"type":"Punctuation","value":")"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"{"}, + {"type":"Text","value":"\n "}, + {"type":"Keyword","value":"var"}, + {"type":"Text","value":" "}, + {"type":"NameProperty","value":"holder3"}, + {"type":"Punctuation","value":":"}, + {"type":"Text","value":" "}, + {"type":"Name","value":"Holder"}, + {"type":"Punctuation","value":"\u003c"}, + {"type":"Name","value":"Int"}, + {"type":"Punctuation","value":","}, + {"type":"Text","value":" "}, + {"type":"Name","value":"String"}, + {"type":"Punctuation","value":","}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"*\u003e?"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"="}, + {"type":"Text","value":" "}, + {"type":"Keyword","value":"null"}, + {"type":"Text","value":"\n "}, + {"type":"Keyword","value":"fun"}, + {"type":"Text","value":" "}, + {"type":"NameFunction","value":"example"}, + {"type":"Punctuation","value":"()"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"{"}, + {"type":"Text","value":"\n "}, + {"type":"Keyword","value":"var"}, + {"type":"Text","value":" "}, + {"type":"NameProperty","value":"holder4"}, + {"type":"Punctuation","value":":"}, + {"type":"Text","value":" "}, + {"type":"Name","value":"Holder"}, + {"type":"Punctuation","value":"\u003c"}, + {"type":"Name","value":"Int"}, + {"type":"Punctuation","value":","}, + {"type":"Text","value":" "}, + {"type":"Name","value":"String"}, + {"type":"Punctuation","value":","}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"*\u003e?"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"="}, + {"type":"Text","value":" "}, + {"type":"Keyword","value":"null"}, + {"type":"Text","value":"\n "}, + {"type":"Punctuation","value":"}"}, + {"type":"Text","value":"\n"}, + {"type":"Punctuation","value":"}"}, + {"type":"Text","value":"\n"}, + {"type":"Keyword","value":"fun"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"\u003c"}, + {"type":"NameClass","value":"T"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":":"}, + {"type":"Text","value":" "}, + {"type":"NameClass","value":"Comparable"}, + {"type":"Punctuation","value":"\u003c"}, + {"type":"NameClass","value":"T"}, + {"type":"Punctuation","value":"\u003e\u003e"}, + {"type":"Text","value":" "}, + {"type":"NameFunction","value":"sort"}, + {"type":"Punctuation","value":"("}, + {"type":"Name","value":"list"}, + {"type":"Punctuation","value":":"}, + {"type":"Text","value":" "}, + {"type":"Name","value":"List"}, + {"type":"Punctuation","value":"\u003c"}, + {"type":"Name","value":"T"}, + {"type":"Punctuation","value":"\u003e)"}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"{"}, + {"type":"Text","value":"\n \n"}, + {"type":"Punctuation","value":"}"}, + {"type":"Text","value":"\n\n"}, {"type":"Keyword","value":"class"}, {"type":"Text","value":" "}, {"type":"NameClass","value":"X"}, @@ -434,7 +759,13 @@ {"type":"Keyword","value":"inline"}, {"type":"Text","value":" "}, {"type":"Keyword","value":"fun"}, - {"type":"Text","value":" \u003creified T\u003e "}, + {"type":"Text","value":" "}, + {"type":"Punctuation","value":"\u003c"}, + {"type":"Keyword","value":"reified"}, + {"type":"Text","value":" "}, + {"type":"NameClass","value":"T"}, + {"type":"Punctuation","value":"\u003e"}, + {"type":"Text","value":" "}, {"type":"NameFunction","value":"generic"}, {"type":"Punctuation","value":"("}, {"type":"Name","value":"t"},