This commit is contained in:
@@ -0,0 +1,315 @@
|
||||
# -*- coding: utf-8 -*- #
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Rouge
|
||||
module Lexers
|
||||
# IMPORTANT NOTICE:
|
||||
#
|
||||
# Please do not copy this lexer and open a pull request
|
||||
# for a new language. It will not get merged, you will
|
||||
# be unhappy, and kittens will cry.
|
||||
#
|
||||
class Javascript < RegexLexer
|
||||
title "JavaScript"
|
||||
desc "JavaScript, the browser scripting language"
|
||||
|
||||
tag 'javascript'
|
||||
aliases 'js'
|
||||
filenames '*.cjs', '*.js', '*.mjs'
|
||||
mimetypes 'application/javascript', 'application/x-javascript',
|
||||
'text/javascript', 'text/x-javascript'
|
||||
|
||||
# Pseudo-documentation: https://stackoverflow.com/questions/1661197/what-characters-are-valid-for-javascript-variable-names
|
||||
|
||||
def self.detect?(text)
|
||||
return 1 if text.shebang?('node')
|
||||
return 1 if text.shebang?('jsc')
|
||||
# TODO: rhino, spidermonkey, etc
|
||||
end
|
||||
|
||||
state :multiline_comment do
|
||||
rule %r([*]/), Comment::Multiline, :pop!
|
||||
rule %r([^*/]+), Comment::Multiline
|
||||
rule %r([*/]), Comment::Multiline
|
||||
end
|
||||
|
||||
state :comments_and_whitespace do
|
||||
rule %r/\s+/, Text
|
||||
rule %r/<!--/, Comment # really...?
|
||||
rule %r(//.*?$), Comment::Single
|
||||
rule %r(/[*]), Comment::Multiline, :multiline_comment
|
||||
end
|
||||
|
||||
state :expr_start do
|
||||
mixin :comments_and_whitespace
|
||||
|
||||
rule %r(/) do
|
||||
token Str::Regex
|
||||
goto :regex
|
||||
end
|
||||
|
||||
rule %r/[{]/ do
|
||||
token Punctuation
|
||||
goto :object
|
||||
end
|
||||
|
||||
rule %r//, Text, :pop!
|
||||
end
|
||||
|
||||
state :regex do
|
||||
rule %r(/) do
|
||||
token Str::Regex
|
||||
goto :regex_end
|
||||
end
|
||||
|
||||
rule %r([^/]\n), Error, :pop!
|
||||
|
||||
rule %r/\n/, Error, :pop!
|
||||
rule %r/\[\^/, Str::Escape, :regex_group
|
||||
rule %r/\[/, Str::Escape, :regex_group
|
||||
rule %r/\\./, Str::Escape
|
||||
rule %r{[(][?][:=<!]}, Str::Escape
|
||||
rule %r/[{][\d,]+[}]/, Str::Escape
|
||||
rule %r/[()?]/, Str::Escape
|
||||
rule %r/./, Str::Regex
|
||||
end
|
||||
|
||||
state :regex_end do
|
||||
rule %r/[gimuy]+/, Str::Regex, :pop!
|
||||
rule(//) { pop! }
|
||||
end
|
||||
|
||||
state :regex_group do
|
||||
# specially highlight / in a group to indicate that it doesn't
|
||||
# close the regex
|
||||
rule %r(/), Str::Escape
|
||||
|
||||
rule %r([^/]\n) do
|
||||
token Error
|
||||
pop! 2
|
||||
end
|
||||
|
||||
rule %r/\]/, Str::Escape, :pop!
|
||||
rule %r/\\./, Str::Escape
|
||||
rule %r/./, Str::Regex
|
||||
end
|
||||
|
||||
state :bad_regex do
|
||||
rule %r/[^\n]+/, Error, :pop!
|
||||
end
|
||||
|
||||
def self.keywords
|
||||
@keywords ||= Set.new %w(
|
||||
async await break case catch continue debugger default delete
|
||||
do else export finally from for if import in instanceof new of
|
||||
return super switch this throw try typeof void while yield
|
||||
)
|
||||
end
|
||||
|
||||
def self.declarations
|
||||
@declarations ||= Set.new %w(
|
||||
var let const with function class
|
||||
extends constructor get set static
|
||||
)
|
||||
end
|
||||
|
||||
def self.reserved
|
||||
@reserved ||= Set.new %w(
|
||||
enum implements interface
|
||||
package private protected public
|
||||
)
|
||||
end
|
||||
|
||||
def self.constants
|
||||
@constants ||= Set.new %w(true false null NaN Infinity undefined)
|
||||
end
|
||||
|
||||
def self.builtins
|
||||
@builtins ||= %w(
|
||||
Array Boolean Date Error Function Math netscape
|
||||
Number Object Packages RegExp String sun decodeURI
|
||||
decodeURIComponent encodeURI encodeURIComponent
|
||||
Error eval isFinite isNaN parseFloat parseInt
|
||||
document window navigator self global
|
||||
Promise Set Map WeakSet WeakMap Symbol Proxy Reflect
|
||||
Int8Array Uint8Array Uint8ClampedArray
|
||||
Int16Array Uint16Array Uint16ClampedArray
|
||||
Int32Array Uint32Array Uint32ClampedArray
|
||||
Float32Array Float64Array DataView ArrayBuffer
|
||||
)
|
||||
end
|
||||
|
||||
def self.id_regex
|
||||
/[\p{L}\p{Nl}$_][\p{Word}]*/io
|
||||
end
|
||||
|
||||
id = self.id_regex
|
||||
|
||||
state :root do
|
||||
rule %r/\A\s*#!.*?\n/m, Comment::Preproc, :statement
|
||||
rule %r((?<=\n)(?=\s|/|<!--)), Text, :expr_start
|
||||
mixin :comments_and_whitespace
|
||||
rule %r(\+\+ | -- | ~ | \?\?=? | && | \|\| | \\(?=\n) | << | >>>? | ===
|
||||
| !== )x,
|
||||
Operator, :expr_start
|
||||
rule %r([-<>+*%&|\^/!=]=?), Operator, :expr_start
|
||||
rule %r/[(\[,]/, Punctuation, :expr_start
|
||||
rule %r/;/, Punctuation, :statement
|
||||
rule %r/[)\].]/, Punctuation
|
||||
|
||||
rule %r/`/ do
|
||||
token Str::Double
|
||||
push :template_string
|
||||
end
|
||||
|
||||
# special case for the safe navigation operator ?.
|
||||
# so that we don't start detecting a ternary expr
|
||||
rule %r/[?][.]/, Punctuation
|
||||
|
||||
rule %r/[?]/ do
|
||||
token Punctuation
|
||||
push :ternary
|
||||
push :expr_start
|
||||
end
|
||||
|
||||
rule %r/(\@)(\w+)?/ do
|
||||
groups Punctuation, Name::Decorator
|
||||
push :expr_start
|
||||
end
|
||||
|
||||
rule %r/(class)((?:\s|\\\s)+)/ do
|
||||
groups Keyword::Declaration, Text
|
||||
push :classname
|
||||
end
|
||||
|
||||
rule %r/([\p{Nl}$_]*\p{Lu}[\p{Word}]*)[ \t]*(?=(\(.*\)))/m, Name::Class
|
||||
|
||||
rule %r/(function)((?:\s|\\\s)+)(#{id})/ do
|
||||
groups Keyword::Declaration, Text, Name::Function
|
||||
end
|
||||
|
||||
rule %r/function(?=(\(.*\)))/, Keyword::Declaration # For anonymous functions
|
||||
|
||||
rule %r/(#?#{id})[ \t]*(?=(\(.*\)))/m do |m|
|
||||
if self.class.keywords.include?(m[1])
|
||||
# "if" in "if (...)" or "switch" in "switch (...)" are recognized as keywords.
|
||||
token Keyword
|
||||
else
|
||||
token Name::Function
|
||||
end
|
||||
end
|
||||
|
||||
rule %r/[{}]/, Punctuation, :statement
|
||||
|
||||
rule %r/#?#{id}/ do |m|
|
||||
if self.class.keywords.include? m[0]
|
||||
token Keyword
|
||||
push :expr_start
|
||||
elsif self.class.declarations.include? m[0]
|
||||
token Keyword::Declaration
|
||||
push :expr_start
|
||||
elsif self.class.reserved.include? m[0]
|
||||
token Keyword::Reserved
|
||||
elsif self.class.constants.include? m[0]
|
||||
token Keyword::Constant
|
||||
elsif self.class.builtins.include? m[0]
|
||||
token Name::Builtin
|
||||
else
|
||||
token Name::Other
|
||||
end
|
||||
end
|
||||
|
||||
rule %r/[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?/, Num::Float
|
||||
rule %r/0x[0-9a-fA-F]+/i, Num::Hex
|
||||
rule %r/0o[0-7][0-7_]*/i, Num::Oct
|
||||
rule %r/0b[01][01_]*/i, Num::Bin
|
||||
rule %r/[0-9]+/, Num::Integer
|
||||
|
||||
rule %r/"/, Str::Delimiter, :dq
|
||||
rule %r/'/, Str::Delimiter, :sq
|
||||
rule %r/:/, Punctuation
|
||||
end
|
||||
|
||||
state :dq do
|
||||
rule %r/\\[\\nrt"]?/, Str::Escape
|
||||
rule %r/[^\\"]+/, Str::Double
|
||||
rule %r/"/, Str::Delimiter, :pop!
|
||||
end
|
||||
|
||||
state :sq do
|
||||
rule %r/\\[\\nrt']?/, Str::Escape
|
||||
rule %r/[^\\']+/, Str::Single
|
||||
rule %r/'/, Str::Delimiter, :pop!
|
||||
end
|
||||
|
||||
state :classname do
|
||||
rule %r/(#{id})((?:\s|\\\s)+)(extends)((?:\s|\\\s)+)/ do
|
||||
groups Name::Class, Text, Keyword::Declaration, Text
|
||||
end
|
||||
|
||||
rule id, Name::Class, :pop!
|
||||
end
|
||||
|
||||
# braced parts that aren't object literals
|
||||
state :statement do
|
||||
rule %r/case\b/ do
|
||||
token Keyword
|
||||
goto :expr_start
|
||||
end
|
||||
|
||||
rule %r/(#{id})(\s*)(:)/ do
|
||||
groups Name::Label, Text, Punctuation
|
||||
end
|
||||
|
||||
mixin :expr_start
|
||||
end
|
||||
|
||||
# object literals
|
||||
state :object do
|
||||
mixin :comments_and_whitespace
|
||||
|
||||
rule %r/[{]/ do
|
||||
token Punctuation
|
||||
push
|
||||
end
|
||||
|
||||
rule %r/[}]/ do
|
||||
token Punctuation
|
||||
goto :statement
|
||||
end
|
||||
|
||||
rule %r/(#{id})(\s*)(:)/ do
|
||||
groups Name::Attribute, Text, Punctuation
|
||||
push :expr_start
|
||||
end
|
||||
|
||||
rule %r/:/, Punctuation
|
||||
mixin :root
|
||||
end
|
||||
|
||||
# ternary expressions, where <id>: is not a label!
|
||||
state :ternary do
|
||||
rule %r/:/ do
|
||||
token Punctuation
|
||||
goto :expr_start
|
||||
end
|
||||
|
||||
mixin :root
|
||||
end
|
||||
|
||||
# template strings
|
||||
state :template_string do
|
||||
rule %r/[$]{/, Punctuation, :template_string_expr
|
||||
rule %r/`/, Str::Double, :pop!
|
||||
rule %r/\\[$`\\]/, Str::Escape
|
||||
rule %r/[^$`\\]+/, Str::Double
|
||||
rule %r/[\\$]/, Str::Double
|
||||
end
|
||||
|
||||
state :template_string_expr do
|
||||
rule %r/}/, Punctuation, :pop!
|
||||
mixin :root
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user