模式参考


数据类型

文章中相关属性值的类型说明:

mode一个有效的 Highlight.js 模式(由本文所定义)
scope有效的语法作用域引用:title.class.inherited
regexpJavaScript 正则表达式字面量(建议使用)或表示正则表达式的字符串(注意,使用字符串时,一定要确保字符正确转义)
booleanJavaScript 布尔值:truefalse
stringJavaScript 字符串
numberJavaScript 数字
objectJavaScript 对象:{ ... }
arrayJavaScript 数组:[ ... ]

语言属性


下列属性仅在语言层上有效(即它们只能存在于最顶层的语言对象上,在子模型中即使指定了也是没有意义的)。

name
  • 类型:string

该编程语言的规范名称,比如 “JavaScript” 等。

unicodeRegex
  • 类型:boolean

表示本语言的语法是否使用 Unicode (u 标志)正则表达式。(默认为 false)译者注:使用 Unicode 正则表达式的主要原因是它可以匹配 Unicode 字符集,而不仅仅是 ASCII 字符集。例如 ECMAScript6(ES6)的语法规范就需要支持 Unicode 字符集

case_insensitive
  • 类型:boolean

若设为 true 则语言关键字和正则表达式对大小写不敏感。仅适用于顶层模式。(默认为 false)

aliases
  • 类型:字符串数组

一个别名列表(文件名不用列入该数组,一般情况下文件名就是语言的标准名称),在 HTML 类或 getLanguage 调用中,别名都会识别为其对应的语言。

classNameAliases
  • 类型:object

语法中自定义 scope 名称与其等价量之间的映射表。举个例子,你的语言中假如存在 "slot" 这个概念,它大致等价于其他语言中的变量,对于这种情况你可以如下实现:

{
  classNameAliases: {
    slot: "variable",
    "message-name": "string"
  },
  contains: [
    {
      scope: "slot",
      begin: // ...
    }
  ]
}

最终的 HTML 输出将会把 slot 渲染为一个 hljs-variable 的 CSS 类。该功能是为了降低维护难度,因为这让语法维护者可以更轻松地使用自己熟悉的语法去维护一门新语言。

有关所有已支持的 scope 名称列表,请参阅作用域引用

disableAutodetect
  • 类型:boolean

该语言禁用自动检测。(默认为 false,表示启用自动检测)

compilerExtensions
警告:这很大程度上取决于编译器的内部结构,而且在小版本之间发布可能并不稳定。目前只推荐用于“第一方”语法。(译者注:“第一方”语法是指由项目的维护者或官方开发团队提供和维护的语法规则。官方提供的语法规则通常比社区贡献的规则更全面、更准确。)
  • 类型:编译器扩展的数组,即:(mode, parentMode) -> {}

该属性使得语法可以扩展模式编译器以添加自己的编程语言特性,从而使语法的读写更容易。我们设计该属性的本意是用它来“测试”新的编译器扩展,如果它们运作良好,就将它们推广到核心库中。

mode

输入的模式对象

parentMode

该模式的父模式(对于顶层语言模式改值为 null)

下面让我们来看一个小扩展作为示例,它很好地实现了 match 这一语法特征,正如我们要表达的本意:匹配单个内容,然后结束模式。

compilerExtensions: [
  (mode, _parentMode) => {
    // first some quick sanity checks
    if (!mode.match) return;

    // then check for users doing things that would make no sense
    if (mode.begin || mode.end) throw new Error("begin & end are not supported with match");

    // copy the match regex into begin
    mode.begin = mode.match;

    // cleanup: delete our syntactic construct
    delete mode.match;
  }
]

编译器扩展函数无返回值,它本来就是只修改模式本身。

模式属性


className

自版本 11.0 起已弃用:请改用 scope

scope

版本 11.0 推出。

  • 类型:scope

一个给定模式的作用域引用。在 HTML 标记中,scope 被转换为 CSS 类名。

多个模式可以具有相同的 scope。当一种语言可以用多种形式来描述同一种语法项时,这就很有用了,比如单引号或双引号之间的内容都是用来描述字符串的。

{
  scope: "title.function.call",
  begin: /[a-z]+\(/
}

有关 scope 和 CSS 类的详细信息,请参阅作用域引用

begin
  • 类型:正则表达式或正则表达式数组

匹配模式起始部分的正则表达式。例如,由一个单引号开头的是字符串,或者由双斜杆开头表示 C 语言风格的注释。如果留空,begin 默认为一个匹配任何内容的正则表达式,也就是说模式会立即成立。

该值也可以是一个数组。参阅 beginScope

beginScope

版本 11.0 开始推出。

  • 类型:scope
  • 类型:scope 的索引数字(当 begin 是一个数组时)

这可以用来将一个 scope 只应用于开始匹配部分。

{
  begin: /def/,
  beginScope: "keyword"
}

你也可以使用 beginScope,通过向 begin 传入一个数组来单独对不同作用域的匹配部分进行高亮处理。

{
begin: [
  /function!/,
  /\s+/,
  hljs.IDENT_RE
],
beginScope: {
  1: "keyword",
  3: "title"
},
}

上述示例会把 function! 作为 keyword 进行高亮处理,以及会把函数名视为 title 处理。空白符也会匹配到,但不会进行任何高亮处理。译者注:hljs.IDENT_RE 实现为 /[$_a-zA-Z][$\w]*/,常用于函数名或变量名的匹配

注意:在内部,数组中的每个正则表达式都会成为一个个的捕获组,然后串联起来成为为一个更复杂的正则表达式。如果你的正则表达式中已经使用了捕获组(或引用),那它们将自动重新枚举,这样便于它们在不用做任何更改的情况下继续工作。

更多信息请参考问题 #3095

endScope

版本 11.0 开始推出。

  • 类型:scope
  • 类型:scope 的索引数字(当 end 是一个数组时)

该属性与 beginScope 功能一样,不过适用于对内容进行 end 匹配。

{
  begin: /FIRST/,
  end: /LAST/,
  endScope: "built_in"
}
match

版本 11.0 开始推出。

  • 类型:正则表达式或正则表达式数组

在不需要 end 表达式时,该属性只是 begin语法糖。它不应该与 beginend 搭配使用(这样的用法没有意义)。设计这个属性仅仅是为了提高语法的可读性。

{
  scope: "title",
  match: /Fish/
}

这等同于:

{
  scope: "title",
  begin: /Fish/
}
on:begin
  • 类型:回调函数 (matchData, response)

该回调在检测到 begin 匹配时触发。matchData 包含了基本的正则匹配数据、完整匹配、匹配组等。response 对象用于告诉解析器应该如何处理匹配。它也可以用来临时存储数据。

  • response.data - 一个用来存储数据的简单对象。它可以用来构建更复杂的规则,尤其当 end 表达式的规则是基于 begin 的内容时,以及一些其他情况。
  • response.ignoreMatch() - 该接口用来忽略当前匹配结果。调用该接口后不会进入到当前模式,而是继续对当前 contains 列表中的子模式进行处理。

相关用法示例,请参阅 modes.js 中的 END_SAME_AS_BEGIN

end
  • 类型:正则表达式

模式中用作结束的正则表达式。例如,一般字符串的结尾可以用单引号表示,又比如 “$” 在一些情况下表示单行注释的结尾(行末)。

通常情况下,一个 begin 类型的正则表达式就能定义整个模式,不需要任何特殊的 end 类型表达式。例如,可以用 begin: "\\b\\d+" 来定义一个数字,该表达式涵盖了所有数字。

在缺少 end 表达式的情况下,它会默认使用一个匹配任何内容的正则表达式,因此该模式可以立即结束(若 contains 有子模式,会在匹配子模式之后)。

有时,一个模式可以不由自身结束,而是隐含地与它的包含模式(父模式)一起结束。我们是通过 endsWithParent 属性实现上述情况的。

on:end
  • 类型:回调 (matchData, response)

该回调函数会在检测到 end 表达式匹配时触发。matchData 包含了基本的正则匹配数据、完整匹配、匹配组等。response 对象用于告诉解析器应该如何处理匹配。它也可以用来检索从 begin 回调中存储的数据。

  • response.data - 一个用来存储数据的简单对象。它可以用来构建更复杂的规则,由其当 end 表达式的规则是基于 begin 的内容时,以及一些其他情况。
  • response.ignoreMatch() - 该接口用来忽略当前匹配结果。调用该接口后不会进入到当前模型,而是继续对当前模型 contains 列表中的子模型进行处理。

相关用法示例,请参阅 modes.js 中的 END_SAME_AS_BEGIN

beginKeywords
  • 类型:string

对于以关键字开头的模型,该属性可以替代 begin 属性,可以减少不必要的重复工作:

{
  begin: '\\b(class|interface)\\b',
  keywords: 'class interface'
}

…... 通常可以缩写为:

{
  beginKeywords: 'class interface'
}

keywords 属性不同,该属性只允许简单地用单个空格来分隔关键字列表。如果你确实需要 keywords 属性的其他功能或需要为本模型定义更多关键字,那么可以将 keywordsbeginKeywords 一起使用。

注意

beginKeywords 还会在关键字前后检查 .,如果找到则匹配失败。这是为了避免方法调用或属性访问时出现误报。译者注:在某些编程语言中,"." 可以用于表示方法调用或属性访问。如果不检查 ".",那么当 beginKeywords 中包含的关键字在一个方法调用或属性访问中出现时,该模式就会被错误地匹配。

例如,class A { ... } 可以匹配,而 A.class == B.class 则不匹配。

endsWithParent
  • 类型:boolean

该标志表示在该模式的父模式结束时一起结束。

通过以下示例可以更好地理解这一属性。在CSS语法中,选择器通常包含在 “{” 和 “}” 符号中,并包含一组规则。每条规则用分号 “;” 分隔,但是最后一条规则可以省略结尾的分号。例如:

p {
  width: 100%;
  color: red
}

若简单地用 end: /;/ 规则,在遇到缺少分号的情况,就会产生问题 —— 解析器可能会“卡住”去寻找那个它无法找到的 ;(或找到后面不属于该选择器的分号),然后一些本该高亮处理的有效内容也被跳过。这种情况下 endsWithParent 属性就很有用了:

{
  scope: 'rules', begin: /\{/, end: /\}/,
  contains: [
    {scope: 'rule', /* ... */ end: ';', endsWithParent: true}
  ]
}

这个 rule scope 现在将在解析器看到本模型的 ; 或其父模型的 } 时都会结束。

endsParent
  • 类型:boolean

在当前模式关闭后,强制关闭父模式。

This is used for modes that don’t have an easily expressible ending lexeme but instead could be closed after the last interesting sub-mode is found.

Here’s an example with two ways of defining functions in Elixir, one using a keyword do and another using a comma:

def foo :clear, list do
  :ok
end

def foo, do: IO.puts "hello world"

Note that in the first case the parameter list after the function title may also include a comma. And if we’re only interested in highlighting a title we can tell it to end the function definition after itself:

{
  scope: 'function',
  beginKeywords: 'def', end: hljs.MATCH_NOTHING_RE,
  contains: [
    {
      scope: 'title',
      begin: hljs.IDENT_RE, endsParent: true
    }
  ]
}

The end: hljs.MATCH_NOTHING_RE ensures that function will never end itself.

keywords
  • type: object / string / array

Keyword definition comes in three forms.

A string of space-separated keywords with an optional relevance following a pipe (|):

'for while if|0 else weird_voodoo|10 ...'

An array of keywords (with optional relevance following a |):

[
  "for",
  "while",
  "if|0"
]

Note

It’s recommended that the array form be used (one keyword per line) rather than a string to simplify future maintenance. This is the style followed by grammars part of the core library.

An object that describing multiple sets of keywords and (optionally) the pattern used to locate them:

{
  keyword: [ 'for', 'while', 'if|0' ],
  literal: [ 'true', 'false' ],
  $pattern: /\w+/
}

For a more detailed explanation see Language definition guide.

illegal
  • type: regexp or array

A regular expression or array that defines symbols illegal for the mode. When the parser finds an illegal match it may immediately stop parsing the whole language altogether (see ignoreIllegals). Smart use of illegal can greatly improve auto-detection by quickly ruling out a language (when an illegal match is found).

{
  illegal: /%/,
  // or using an array
  illegal: [ /%/, /cookies/ ]
}
excludeBegin, excludeEnd
  • type: boolean

Excludes beginning or ending matches from a mode’s content. For example in CSS syntax a rule ends with a semicolon. However visually it’s better not to consider the semicolon as part of the rule’s contents. Using excludeEnd: true forces a <span> element for the rule to close before the semicolon.

The semicolon is still consumed by the rule though and cannot be matched by other subsequent rules. (it’s effectively been skipped over)

returnBegin
  • type: boolean

Returns just found beginning lexeme back into parser. This is used when beginning of a sub-mode is a complex expression that should not only be found within a parent mode but also parsed according to the rules of a sub-mode.

Warning: Since the parser is effectively goes back it’s quite possible to create a infinite loop here so use with caution! A look-ahead regex is almost always preferable.
returnEnd
  • type: boolean

Returns just found ending lexeme back into parser. This is used for example to parse JavaScript embedded into HTML. A JavaScript block ends with the HTML closing tag </script> that cannot be parsed with JavaScript rules. So it is returned back into its parent HTML mode that knows what to do with it.

Warning: Since the parser is effectively goes back it’s quite possible to create a infinite loop here so use with caution! A look-ahead regex is almost always preferable.
contains
  • type: array

The list of sub-modes that can be found inside the mode. For detailed explanation see Language definition guide.

starts
  • type: mode

The the mode that will start right after the current mode ends. The new mode will not be contained within the current one.

Currently this attribute is used to highlight JavaScript and CSS contained within HTML. Tags <script> and <style> start sub-modes that use another language definition to parse their contents (see subLanguage).

variants
  • type: array

Modification to the main definitions of the mode, effectively expanding it into several similar modes each having all the attributes from the main definition augmented or overridden by the variants:

{
  scope: 'string',
  contains: ['self', hljs.BACKSLASH_ESCAPE],
  relevance: 0,
  variants: [
    {begin: /"/, end: /"/},
    {begin: /'/, end: /'/, relevance: 1}
  ]
}

Note

variants has very specific behavior with regards to contains: ['self']. Lets consider the example above. While you might think this would allow you to embed any type of string (double or single quoted) within any other string, it does not.

The variants are instead compiled into to two discrete modes:

{ scope: 'string', begin: /"/, contains: ['self', ... ] }{ scope: 'string', begin: /'/, contains: ['self', ... ] }

Each mode’s self refers only to the new expanded mode, not the original mode with variants (which no longer exists after compiling).

Further info: https://github.com/highlightjs/highlight.js/issues/826

subLanguage
  • type: string or array

Highlights the entire contents of the mode with another language.

When using this attribute there’s no point to define internal parsing rules like keywords, etc. Also it is recommended to avoid the scope attribute since the sublanguage already wraps the text in its own <span class="language-name"> tag.

The value of the attribute controls which language or languages will be used for highlighting:

  • language name: explicit highlighting with the specified language
  • empty array: auto detection with all the languages available
  • array of language names: auto detection constrained to the specified set
skip
  • 类型:boolean

Skips any markup processing for the mode ensuring that it remains a part of its parent buffer along with the starting and the ending lexemes. This works in conjunction with the parent’s subLanguage when it requires complex parsing.

Consider parsing PHP inside HTML:

<p><? echo 'PHP'; /* ?> */ ?></p>

The ?> inside the comment should not end the PHP part, so we have to handle pairs of /* .. */ to correctly find the ending ?>:

{
  begin: /<\?/, end: /\?>/,
  subLanguage: 'php',
  contains: [{begin: '/\\*', end: '\\*/', skip: true}]
}

Without skip: true every comment would cause the parser to drop out back into the HTML mode.