From 8defb26516e72915ba38f74286b2ddce2cf99aa5 Mon Sep 17 00:00:00 2001 From: Sergey Mashkov Date: Sun, 6 Aug 2017 02:09:32 +0300 Subject: [PATCH 1/5] Kotlin: initial scanner implementation --- lib/coderay/helpers/file_type.rb | 2 + lib/coderay/scanners/kotlin.rb | 163 +++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 lib/coderay/scanners/kotlin.rb diff --git a/lib/coderay/helpers/file_type.rb b/lib/coderay/helpers/file_type.rb index 7de34d58..09819cca 100644 --- a/lib/coderay/helpers/file_type.rb +++ b/lib/coderay/helpers/file_type.rb @@ -97,6 +97,8 @@ def type_from_shebang filename 'java' => :java, 'js' => :java_script, 'json' => :json, + 'kt' => :kotlin, + 'kts' => :kotlin, 'lua' => :lua, 'mab' => :ruby, 'pas' => :delphi, diff --git a/lib/coderay/scanners/kotlin.rb b/lib/coderay/scanners/kotlin.rb new file mode 100644 index 00000000..aca3b3d7 --- /dev/null +++ b/lib/coderay/scanners/kotlin.rb @@ -0,0 +1,163 @@ +module CodeRay + module Scanners + + load :java + + class Kotlin < Java + + register_for :kotlin + file_extension 'kt' + + KOTLIN_KEYWORDS = %w[ + package import + as? as is + val var + class interface object fun init get set + in out + if when else for while do return break continue + ] + + KOTLIN_MODIFIERS = %w[ + annotation enum data sealed companion + abstract open final + public protected private internal + inline suspend + inner + ] + + TYPES = %w[ + Boolean Byte Char class Double Float Int Long Short Unit Nothing Any + ] + + STRING_CONTENT_PATTERN = { + "'" => /[^\\'$]+/, + '"' => /[^\\"$]+/, + } # :nodoc:s + + IDENT_KIND = Java::IDENT_KIND.dup. + add(TYPES, :type). + add(KOTLIN_KEYWORDS, :keyword). + add(KOTLIN_MODIFIERS, :keyword) # :nodoc: + + + def setup + @state = :initial + end + + def scan_tokens encoder, options + string_delimiter = nil + state = options[:state] || @state + last_token_dot = false + class_name_follows = false + delimiters = [] + + until eos? + + case state + + when :initial + if (match = scan(/ \s+ | \\\n /x)) + encoder.text_token match, :space + next + elsif (match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)) + encoder.text_token match, :comment + next + elsif (match = scan(/ TODO \( /ox)) + encoder.text_token "TODO", :comment + encoder.text_token "(", :operator + elsif (match = scan(/ #{IDENT} /ox)) + kind = IDENT_KIND[match] + if last_token_dot + kind = :ident + elsif class_name_follows + kind = :class + class_name_follows = false + else + case match + when 'import' + package_name_expected = :include + when 'package' + package_name_expected = :namespace + when 'class', 'interface' + class_name_follows = true + else + # nothing + end + end + encoder.text_token match, kind + elsif (match = scan(/ \.(?!\d) | [,?:()\[\]] | -- | \+\+ | && | \|\| | \*\*=? | [-+*\/%^~&|<>=!]=? /x)) + encoder.text_token match, :operator + elsif (match = scan(/{/)) + class_name_follows = false + encoder.text_token match, :operator + elsif (match = scan(/}/)) + encoder.text_token match, :operator + + unless delimiters.empty? + string_delimiter = delimiters.pop + encoder.end_group state + state = :string + end + elsif (match = scan(/["']/)) + state = :string + encoder.begin_group state + string_delimiter = match + encoder.text_token match, :delimiter + elsif check(/[\d.]/) + if (match = scan(/0[xX][0-9A-Fa-f]+/)) + encoder.text_token match, :hex + elsif (match = scan(/(?>0[0-7]+)(?![89.eEfF])/)) + encoder.text_token match, :octal + elsif (match = scan(/\d+[fFdD]|\d*\.\d+(?:[eE][+-]?\d+)?[fFdD]?|\d+[eE][+-]?\d+[fFdD]?/)) + encoder.text_token match, :float + elsif (match = scan(/\d+[lL]?/)) + encoder.text_token match, :integer + end + + elsif (match = scan(/ @ #{IDENT} /ox)) + encoder.text_token match, :annotation + + else + encoder.text_token getch, :error + end + + when :string + if (match = scan(/\${/)) + encoder.text_token match, :operator + + state = :initial + encoder.begin_group state + + delimiters << string_delimiter + string_delimiter = nil + elsif (match = scan(/ \$ #{IDENT} /ox)) + encoder.text_token match, :ident + elsif (match = scan(STRING_CONTENT_PATTERN[string_delimiter])) + encoder.text_token match, :content + elsif (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)) + if string_delimiter == "'" && !(match == "\\\\" || match == "\\'") + encoder.text_token match, :content + else + encoder.text_token match, :char + end + elsif (match = scan(/["']/)) + encoder.text_token match, :delimiter + encoder.end_group state + state = :initial + string_delimiter = nil + elsif (match = scan(/ \\ | $ /x)) + encoder.end_group state + state = :initial + encoder.text_token match, :error unless match.empty? + else + raise_inspect "else case \" reached; %p not handled." % peek(1), encoder + end + else + raise_inspect 'Unknown state', encoder + end + + end + end + end + end +end From 63c5556ab6a9c6bca053275ca4fb876cac88fa4e Mon Sep 17 00:00:00 2001 From: Sergey Mashkov Date: Sun, 6 Aug 2017 02:09:52 +0300 Subject: [PATCH 2/5] Kotlin: prepare smoke test --- test/executable/source.kt | 31 +++++++++++++++++++++++++++++++ test/executable/suite.rb | 21 +++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 test/executable/source.kt diff --git a/test/executable/source.kt b/test/executable/source.kt new file mode 100644 index 00000000..4bcaa378 --- /dev/null +++ b/test/executable/source.kt @@ -0,0 +1,31 @@ +class Test { + val text1: String = "abc \' def \u0000 \n \" hehe " + val text2: List = listOf('a', '\n', '\'', '"', '\"') + val numbers = listOf(0, 12, 1.0f, 1.0, 1L, 0x1f, -1, -12, -1.0f, -1.0, -1L) + + val template = "abc${1 + "b"}def" + val template2 = "abc${1 + 'b'}def" + val template3 = "abc $var def" + + var v: Int = 0 + + fun function(): ReturnType { + } + + fun parametrizedFunction(): T = TODO() + + fun references() { + super.references() + this.references() + } + + @Annotation + class Annotated + + inner class Inner + + object O + + companion object { + } +} \ No newline at end of file diff --git a/test/executable/suite.rb b/test/executable/suite.rb index 997405ca..f490a497 100644 --- a/test/executable/suite.rb +++ b/test/executable/suite.rb @@ -194,6 +194,27 @@ def coderay args, options = {} end end + context 'highlighting a file without explicit input type (source.kt)' do + source_file = ROOT_DIR + 'test/executable/source.kt' + command = "#{source_file} -html" + + source = File.read source_file + + pre = %r{
(.*?)
}m + tag_class = /]*>/ + + should 'generate json' do + target = coderay("#{source_file} #{source_file}.json") + target = coderay("#{source_file} #{source_file}.html") + end + + # should 'respect the file extension and highlight the input as Kotlin' do + # target = coderay(command) + # assert_equal %w(keyword class), target[pre, 1].scan(tag_class).flatten + # end + + end + context 'highlighting a file with explicit input and output type (-ruby source.py -span)' do source_file = ROOT_DIR + 'test/executable/source.py' command = "-ruby #{source_file} -span" From 10ae5207bcc064abc3c7bd9200c23233f3d27247 Mon Sep 17 00:00:00 2001 From: Sergey Mashkov Date: Sun, 6 Aug 2017 22:42:13 +0300 Subject: [PATCH 3/5] Kotlin: implement multiline strings, fix nested braces --- lib/coderay/scanners/kotlin.rb | 41 ++++++++++++++++++++++++++++++---- test/executable/source.kt | 7 ++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/lib/coderay/scanners/kotlin.rb b/lib/coderay/scanners/kotlin.rb index aca3b3d7..40cd3740 100644 --- a/lib/coderay/scanners/kotlin.rb +++ b/lib/coderay/scanners/kotlin.rb @@ -50,6 +50,7 @@ def scan_tokens encoder, options last_token_dot = false class_name_follows = false delimiters = [] + states = [] until eos? @@ -90,14 +91,22 @@ def scan_tokens encoder, options elsif (match = scan(/{/)) class_name_follows = false encoder.text_token match, :operator + states << :initial elsif (match = scan(/}/)) encoder.text_token match, :operator - unless delimiters.empty? - string_delimiter = delimiters.pop - encoder.end_group state - state = :string + unless states.empty? + state = states.pop + + if state == :string || state == :multiline_string + string_delimiter = delimiters.pop + encoder.end_group :initial + end end + elsif (match = scan(/"""/)) + state = :multiline_string + encoder.begin_group :string + encoder.text_token match, :delimiter elsif (match = scan(/["']/)) state = :string encoder.begin_group state @@ -129,6 +138,7 @@ def scan_tokens encoder, options encoder.begin_group state delimiters << string_delimiter + states << :string string_delimiter = nil elsif (match = scan(/ \$ #{IDENT} /ox)) encoder.text_token match, :ident @@ -152,6 +162,29 @@ def scan_tokens encoder, options else raise_inspect "else case \" reached; %p not handled." % peek(1), encoder end + when :multiline_string + if (match = scan(/\${/)) + encoder.text_token match, :operator + + state = :initial + encoder.begin_group state + + delimiters << nil + states << :multiline_string + elsif (match = scan(/ \$ #{IDENT} /ox)) + encoder.text_token match, :ident + elsif (match = scan(/ [^$\\"]+ /x)) + encoder.text_token match, :content + elsif (match = scan(/"""/x)) + encoder.text_token match, :delimiter + encoder.end_group :string + state = :initial + string_delimiter = nil + elsif (match = scan(/"/)) + encoder.text_token match, :content + else + raise_inspect "else case \" reached; %p not handled." % peek(1), encoder + end else raise_inspect 'Unknown state', encoder end diff --git a/test/executable/source.kt b/test/executable/source.kt index 4bcaa378..34332dea 100644 --- a/test/executable/source.kt +++ b/test/executable/source.kt @@ -7,6 +7,13 @@ class Test { val template2 = "abc${1 + 'b'}def" val template3 = "abc $var def" + val multiline = """ first line $var ${1 + 1} + second line + and quotes: ' " '' "" ok + """ + + val innerBraaces = " before ${ if (true) { 1 } else { 2 } }" + var v: Int = 0 fun function(): ReturnType { From b1fbdab4938776dfe44df9895c445ea154f2f4f7 Mon Sep 17 00:00:00 2001 From: Sergey Mashkov Date: Sun, 6 Aug 2017 23:20:09 +0300 Subject: [PATCH 4/5] Kotlin: smoke test and expected test data --- .gitignore | 2 ++ test/executable/source.kt | 2 +- test/executable/source.kt.expected.json | 1 + test/executable/suite.rb | 17 ++++++++--------- 4 files changed, 12 insertions(+), 10 deletions(-) create mode 100644 test/executable/source.kt.expected.json diff --git a/.gitignore b/.gitignore index deed1a27..5c761439 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,7 @@ Gemfile.lock .ruby-version test/executable/source.rb.html test/executable/source.rb.json +test/executable/*.actual.html +test/executable/*.actual.json test/scanners old-stuff diff --git a/test/executable/source.kt b/test/executable/source.kt index 34332dea..290f9d6a 100644 --- a/test/executable/source.kt +++ b/test/executable/source.kt @@ -12,7 +12,7 @@ class Test { and quotes: ' " '' "" ok """ - val innerBraaces = " before ${ if (true) { 1 } else { 2 } }" + val innerBraces = " before ${ if (true) { 1 } else { 2 } }" var v: Int = 0 diff --git a/test/executable/source.kt.expected.json b/test/executable/source.kt.expected.json new file mode 100644 index 00000000..f4f2643b --- /dev/null +++ b/test/executable/source.kt.expected.json @@ -0,0 +1 @@ +[{"type":"text","text":"class","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"Test","kind":"class"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"{","kind":"operator"},{"type":"text","text":"\n ","kind":"space"},{"type":"text","text":"val","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"text1","kind":"ident"},{"type":"text","text":":","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"String","kind":"predefined_type"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"=","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"block","action":"open","kind":"string"},{"type":"text","text":"\"","kind":"delimiter"},{"type":"text","text":"abc ","kind":"content"},{"type":"text","text":"\\'","kind":"char"},{"type":"text","text":" def ","kind":"content"},{"type":"text","text":"\\u0000","kind":"char"},{"type":"text","text":" ","kind":"content"},{"type":"text","text":"\\n","kind":"char"},{"type":"text","text":" ","kind":"content"},{"type":"text","text":"\\\"","kind":"char"},{"type":"text","text":" hehe ","kind":"content"},{"type":"text","text":"\"","kind":"delimiter"},{"type":"block","action":"close","kind":"string"},{"type":"text","text":"\n ","kind":"space"},{"type":"text","text":"val","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"text2","kind":"ident"},{"type":"text","text":":","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"List","kind":"predefined_type"},{"type":"text","text":"<","kind":"operator"},{"type":"text","text":"Char","kind":"type"},{"type":"text","text":">","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"=","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"listOf","kind":"ident"},{"type":"text","text":"(","kind":"operator"},{"type":"block","action":"open","kind":"string"},{"type":"text","text":"'","kind":"delimiter"},{"type":"text","text":"a","kind":"content"},{"type":"text","text":"'","kind":"delimiter"},{"type":"block","action":"close","kind":"string"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"block","action":"open","kind":"string"},{"type":"text","text":"'","kind":"delimiter"},{"type":"text","text":"\\n","kind":"content"},{"type":"text","text":"'","kind":"delimiter"},{"type":"block","action":"close","kind":"string"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"block","action":"open","kind":"string"},{"type":"text","text":"'","kind":"delimiter"},{"type":"text","text":"\\'","kind":"char"},{"type":"text","text":"'","kind":"delimiter"},{"type":"block","action":"close","kind":"string"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"block","action":"open","kind":"string"},{"type":"text","text":"'","kind":"delimiter"},{"type":"text","text":"\"","kind":"content"},{"type":"text","text":"'","kind":"delimiter"},{"type":"block","action":"close","kind":"string"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"block","action":"open","kind":"string"},{"type":"text","text":"'","kind":"delimiter"},{"type":"text","text":"\\\"","kind":"content"},{"type":"text","text":"'","kind":"delimiter"},{"type":"block","action":"close","kind":"string"},{"type":"text","text":")","kind":"operator"},{"type":"text","text":"\n ","kind":"space"},{"type":"text","text":"val","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"numbers","kind":"ident"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"=","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"listOf","kind":"ident"},{"type":"text","text":"(","kind":"operator"},{"type":"text","text":"0","kind":"integer"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"12","kind":"integer"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"1.0f","kind":"float"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"1.0","kind":"float"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"1L","kind":"integer"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"0x1f","kind":"hex"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"-","kind":"operator"},{"type":"text","text":"1","kind":"integer"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"-","kind":"operator"},{"type":"text","text":"12","kind":"integer"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"-","kind":"operator"},{"type":"text","text":"1.0f","kind":"float"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"-","kind":"operator"},{"type":"text","text":"1.0","kind":"float"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"-","kind":"operator"},{"type":"text","text":"1L","kind":"integer"},{"type":"text","text":")","kind":"operator"},{"type":"text","text":"\n\n ","kind":"space"},{"type":"text","text":"val","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"template","kind":"ident"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"=","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"block","action":"open","kind":"string"},{"type":"text","text":"\"","kind":"delimiter"},{"type":"text","text":"abc","kind":"content"},{"type":"text","text":"${","kind":"operator"},{"type":"block","action":"open","kind":"initial"},{"type":"text","text":"1","kind":"integer"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"+","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"block","action":"open","kind":"string"},{"type":"text","text":"\"","kind":"delimiter"},{"type":"text","text":"b","kind":"content"},{"type":"text","text":"\"","kind":"delimiter"},{"type":"block","action":"close","kind":"string"},{"type":"text","text":"}","kind":"operator"},{"type":"block","action":"close","kind":"initial"},{"type":"text","text":"def","kind":"content"},{"type":"text","text":"\"","kind":"delimiter"},{"type":"block","action":"close","kind":"string"},{"type":"text","text":"\n ","kind":"space"},{"type":"text","text":"val","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"template2","kind":"ident"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"=","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"block","action":"open","kind":"string"},{"type":"text","text":"\"","kind":"delimiter"},{"type":"text","text":"abc","kind":"content"},{"type":"text","text":"${","kind":"operator"},{"type":"block","action":"open","kind":"initial"},{"type":"text","text":"1","kind":"integer"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"+","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"block","action":"open","kind":"string"},{"type":"text","text":"'","kind":"delimiter"},{"type":"text","text":"b","kind":"content"},{"type":"text","text":"'","kind":"delimiter"},{"type":"block","action":"close","kind":"string"},{"type":"text","text":"}","kind":"operator"},{"type":"block","action":"close","kind":"initial"},{"type":"text","text":"def","kind":"content"},{"type":"text","text":"\"","kind":"delimiter"},{"type":"block","action":"close","kind":"string"},{"type":"text","text":"\n ","kind":"space"},{"type":"text","text":"val","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"template3","kind":"ident"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"=","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"block","action":"open","kind":"string"},{"type":"text","text":"\"","kind":"delimiter"},{"type":"text","text":"abc ","kind":"content"},{"type":"text","text":"$var","kind":"ident"},{"type":"text","text":" def","kind":"content"},{"type":"text","text":"\"","kind":"delimiter"},{"type":"block","action":"close","kind":"string"},{"type":"text","text":"\n\n ","kind":"space"},{"type":"text","text":"val","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"multiline","kind":"ident"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"=","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"block","action":"open","kind":"string"},{"type":"text","text":"\"\"\"","kind":"delimiter"},{"type":"text","text":" first line ","kind":"content"},{"type":"text","text":"$var","kind":"ident"},{"type":"text","text":" ","kind":"content"},{"type":"text","text":"${","kind":"operator"},{"type":"block","action":"open","kind":"initial"},{"type":"text","text":"1","kind":"integer"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"+","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"1","kind":"integer"},{"type":"text","text":"}","kind":"operator"},{"type":"block","action":"close","kind":"initial"},{"type":"text","text":"\n second line\n and quotes: ' ","kind":"content"},{"type":"text","text":"\"","kind":"content"},{"type":"text","text":" '' ","kind":"content"},{"type":"text","text":"\"","kind":"content"},{"type":"text","text":"\"","kind":"content"},{"type":"text","text":" ok\n ","kind":"content"},{"type":"text","text":"\"\"\"","kind":"delimiter"},{"type":"block","action":"close","kind":"string"},{"type":"text","text":"\n\n ","kind":"space"},{"type":"text","text":"val","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"innerBraces","kind":"ident"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"=","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"block","action":"open","kind":"string"},{"type":"text","text":"\"","kind":"delimiter"},{"type":"text","text":" before ","kind":"content"},{"type":"text","text":"${","kind":"operator"},{"type":"block","action":"open","kind":"initial"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"if","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"(","kind":"operator"},{"type":"text","text":"true","kind":"predefined_constant"},{"type":"text","text":")","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"{","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"1","kind":"integer"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"}","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"else","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"{","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"2","kind":"integer"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"}","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"}","kind":"operator"},{"type":"block","action":"close","kind":"initial"},{"type":"text","text":"\"","kind":"delimiter"},{"type":"block","action":"close","kind":"string"},{"type":"text","text":"\n\n ","kind":"space"},{"type":"text","text":"var","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"v","kind":"ident"},{"type":"text","text":":","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"Int","kind":"type"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"=","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"0","kind":"integer"},{"type":"text","text":"\n\n ","kind":"space"},{"type":"text","text":"fun","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"function","kind":"ident"},{"type":"text","text":"(","kind":"operator"},{"type":"text","text":")","kind":"operator"},{"type":"text","text":":","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"ReturnType","kind":"ident"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"{","kind":"operator"},{"type":"text","text":"\n ","kind":"space"},{"type":"text","text":"}","kind":"operator"},{"type":"text","text":"\n\n ","kind":"space"},{"type":"text","text":"fun","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"<","kind":"operator"},{"type":"text","text":"T","kind":"ident"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":":","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"Any","kind":"type"},{"type":"text","text":">","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"parametrizedFunction","kind":"ident"},{"type":"text","text":"(","kind":"operator"},{"type":"text","text":")","kind":"operator"},{"type":"text","text":":","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"T","kind":"ident"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"=","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"TODO","kind":"comment"},{"type":"text","text":"(","kind":"operator"},{"type":"text","text":")","kind":"operator"},{"type":"text","text":"\n\n ","kind":"space"},{"type":"text","text":"fun","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"references","kind":"ident"},{"type":"text","text":"(","kind":"operator"},{"type":"text","text":")","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"{","kind":"operator"},{"type":"text","text":"\n ","kind":"space"},{"type":"text","text":"super","kind":"local_variable"},{"type":"text","text":".","kind":"operator"},{"type":"text","text":"references","kind":"ident"},{"type":"text","text":"(","kind":"operator"},{"type":"text","text":")","kind":"operator"},{"type":"text","text":"\n ","kind":"space"},{"type":"text","text":"this","kind":"local_variable"},{"type":"text","text":".","kind":"operator"},{"type":"text","text":"references","kind":"ident"},{"type":"text","text":"(","kind":"operator"},{"type":"text","text":")","kind":"operator"},{"type":"text","text":"\n ","kind":"space"},{"type":"text","text":"}","kind":"operator"},{"type":"text","text":"\n\n ","kind":"space"},{"type":"text","text":"@Annotation","kind":"annotation"},{"type":"text","text":"\n ","kind":"space"},{"type":"text","text":"class","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"Annotated","kind":"class"},{"type":"text","text":"\n\n ","kind":"space"},{"type":"text","text":"inner","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"class","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"Inner","kind":"class"},{"type":"text","text":"<","kind":"operator"},{"type":"text","text":"in","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"T","kind":"ident"},{"type":"text","text":",","kind":"operator"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"out","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"E","kind":"ident"},{"type":"text","text":">","kind":"operator"},{"type":"text","text":"\n\n ","kind":"space"},{"type":"text","text":"object","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"O","kind":"ident"},{"type":"text","text":"\n\n ","kind":"space"},{"type":"text","text":"companion","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"object","kind":"keyword"},{"type":"text","text":" ","kind":"space"},{"type":"text","text":"{","kind":"operator"},{"type":"text","text":"\n ","kind":"space"},{"type":"text","text":"}","kind":"operator"},{"type":"text","text":"\n","kind":"space"},{"type":"text","text":"}","kind":"operator"}] diff --git a/test/executable/suite.rb b/test/executable/suite.rb index f490a497..bf54cf23 100644 --- a/test/executable/suite.rb +++ b/test/executable/suite.rb @@ -194,18 +194,17 @@ def coderay args, options = {} end end - context 'highlighting a file without explicit input type (source.kt)' do + context 'Kotlin smoke test' do source_file = ROOT_DIR + 'test/executable/source.kt' - command = "#{source_file} -html" - - source = File.read source_file - - pre = %r{
(.*?)
}m - tag_class = /]*>/ should 'generate json' do - target = coderay("#{source_file} #{source_file}.json") - target = coderay("#{source_file} #{source_file}.html") + coderay("#{source_file} #{source_file}.actual.json") + # coderay("#{source_file} #{source_file}.actual.html") + + result = JSON.parse(File.read ("#{source_file}.actual.json")) + expected = JSON.parse(File.read ("#{source_file}.expected.json")) + + assert_equal expected, result end # should 'respect the file extension and highlight the input as Kotlin' do From 356644ed055063de44fe5edb7abddfbe95782c2c Mon Sep 17 00:00:00 2001 From: Sergey Mashkov Date: Mon, 7 Aug 2017 00:26:57 +0300 Subject: [PATCH 5/5] Kotlin: fix regexp escapes --- lib/coderay/scanners/kotlin.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/coderay/scanners/kotlin.rb b/lib/coderay/scanners/kotlin.rb index 40cd3740..a0215e06 100644 --- a/lib/coderay/scanners/kotlin.rb +++ b/lib/coderay/scanners/kotlin.rb @@ -88,11 +88,11 @@ def scan_tokens encoder, options encoder.text_token match, kind elsif (match = scan(/ \.(?!\d) | [,?:()\[\]] | -- | \+\+ | && | \|\| | \*\*=? | [-+*\/%^~&|<>=!]=? /x)) encoder.text_token match, :operator - elsif (match = scan(/{/)) + elsif (match = scan(/\{/)) class_name_follows = false encoder.text_token match, :operator states << :initial - elsif (match = scan(/}/)) + elsif (match = scan(/\}/)) encoder.text_token match, :operator unless states.empty? @@ -131,7 +131,7 @@ def scan_tokens encoder, options end when :string - if (match = scan(/\${/)) + if (match = scan(/\$\{/)) encoder.text_token match, :operator state = :initial @@ -163,7 +163,7 @@ def scan_tokens encoder, options raise_inspect "else case \" reached; %p not handled." % peek(1), encoder end when :multiline_string - if (match = scan(/\${/)) + if (match = scan(/\$\{/)) encoder.text_token match, :operator state = :initial