CodeMirror mode编写

Writing CodeMirror Modes

Modes typically consist of a single JavaScript file. This file defines, in the simplest case, a lexer (tokenizer) for your language—a function that takes a character stream as input, advances it past a token, and returns a style for that token. More advanced modes can also handle indentation for the language.

模式通常有单个js文件组成,在最简单情况下,这个文件给你的语言定义了一个词素(标记器)--已字符流作为输入的函数,使其通过token(token表示什么?),并且返回该token的样式。更高级的模式也可以处理语言的缩进。

 

This section describes the low-level mode interface. Many modes are written directly against this, since it offers a lot of control, but for a quick mode definition, you might want to use the simple mode addon.

本节描述低级模式接口。许多模式都是直接针对此编写,因为它提供了很多控制,但是对于快速模式定义,你可能希望使用简单的模式附加组件(simple mode addon)

 

The mode script should call CodeMirror.defineMode to register itself with CodeMirror. This function takes two arguments. The first should be the name of the mode, for which you should use a lowercase string, preferably one that is also the name of the files that define the mode (i.e. "xml" is defined in xml.js). The second argument should be a function that, given a CodeMirror configuration object (the thing passed to the CodeMirror function) and an optional mode configuration object (as in the mode option), returns a mode object.

脚本模式应该通过调用CodeMirror.defineMode注册,这个函数接收两个参数。第一个参数应该是该mode的名称,你应该使用小写字符串命名,最好也是定义mode的文件的名称之一(例如: 'xml' 由 xml.js定义). 第二个参数应该是一个函数,给定CodeMirror的配置对象(传递给CodeMirror的东西)和模式配置对象(可选, 例如在CodeMirror配置中的mode属性),该函数返回一个mode对象。

 

Typically, you should use this second argument to defineMode as your module scope function (modes should not leak anything into the global scope!), i.e. write your whole mode inside this function.

通常,你应该使用第二个参数定义mode(defineMode)为模块作用域函数(modes不应该将任何内容泄露到全局作用域中!),将你的整个mode写在这个作用域函数中。

 

The main responsibility of a mode script is parsing the content of the editor. Depending on the language and the amount of functionality desired, this can be done in really easy or extremely complicated ways. Some parsers can be stateless, meaning that they look at one element (token) of the code at a time, with no memory of what came before. Most, however, will need to remember something. This is done by using a state object, which is an object that is always passed when reading a token, and which can be mutated by the tokenizer.

模式脚本的主要职责是解析编辑器的内容。根据语言和所需的功能需求量,这可以用非常简单或非常复杂的方式来完成。有些解析器可以是无状态的,这意味着它们一次只查看代码的一个元素(token),而不会存储之前的内容。然而,大多数人都需要记住一些东西。这是通过使用状态对象来实现的,状态对象是在读取token时始终传递的对象,并且可以由token生成器(or 记录器?)对其进行更改。

 

Modes that use a state must define a startState method on their mode object. This is a function of no arguments that produces a state object to be used at the start of a document.

使用状态的modes必须在其mode对象上定义一个startState方法。这是一个不带参数的函数,用来生成要在文档开始时使用的状态对象。

 

The most important part of a mode object is its token(stream, state) method. All modes must define this method. It should read one token from the stream it is given as an argument, optionally update its state, and return a style string, or null for tokens that do not have to be styled. For your styles, you are encouraged to use the 'standard' names defined in the themes (without the cm- prefix). If that fails, it is also possible to come up with your own and write your own CSS theme file.

mode对象最重要的部分是它的 token(stream, state)方法。所有的modes必须定义这个方法。它应该从它的参数stream中读取一个token,可选的更新它的状态,并返回一个样式字符串,或者为不必样式化的token返回null。对于你的风格,鼓励你使用主题(theme)中定义的‘标准’的名称(没有 'cm-'前缀)。如果失败了,也有可能拿出你自己的并且编写你自己的css主题(theme)文件。

 

A typical token string would be "variable" or "comment". Multiple styles can be returned (separated by spaces), for example "string error" for a thing that looks like a string but is invalid somehow (say, missing its closing quote). When a style is prefixed by "line-" or "line-background-", the style will be applied to the whole line, analogous to what the addLineClass method does—styling the "text" in the simple case, and the "background" element when "line-background-" is prefixed.

一个典型的token字符串可能是 "variable" (变量) 或者 "comment"(注释)。可以返回多个样式(用空格分隔),  例如 "string error" 一个看起来像字符串但是无效的(例如,缺少结束的引号)。当样式以 "line-" 或者 "line-background"作为前缀,样式将作用于整行,类似于 addLineClass 方法所做的--在最简单的情况下为 'text' 设置样式, 和在"line-background"作为前缀时为"background"元素设置样式。

The stream object that's passed to token encapsulates a line of code (tokens may never span lines) and our current position in that line. It has the following API:

传递给token的stream对象封装了一行代码(tokens可能永远不会跨行)以及我们在该行当前的位置。它具有以下api:

 

eol() → boolean
Returns true only if the stream is at the end of the line.
仅当stream位于行的末尾时才返回true
sol() → boolean
Returns true only if the stream is at the start of the line.
仅当stream位于行首时才返回true
peek() → string
Returns the next character in the stream without advancing it. Will return a null at the end of the line.
返回stream中的下一个字符而不缩进,将在行的末尾返回null。
next() → string
Returns the next character in the stream and advances it. Also returns null when no more characters are available.
返回stream中的下一个字符并将其前进。当没有更多字符可用时,也返回null。
eat(match: string|regexp|function(char: string) → boolean) → string
match can be a character, a regular expression, or a function that takes a character and returns a boolean. If the next character in the stream 'matches' the given argument, it is consumed and returned. Otherwise, undefined is returned.
match 可以是字符,正则表达式,也可以接受一个字符的函数然后返回一个布尔值。如果stream中的下一个字符和给定的参数匹配,则被消耗并返回。否则,返回undefined。
eatWhile(match: string|regexp|function(char: string) → boolean) → boolean
Repeatedly calls eat with the given argument, until it fails. Returns true if any characters were eaten.
通过给定参数重复调用eat, 直到失败。 如果任一字符被吃掉则返回true。
eatSpace() → boolean
Shortcut for eatWhile when matching white-space.
匹配空白区时eatWhile的捷径。
skipToEnd()
Moves the position to the end of the line.
将位置移动到行尾。
skipTo(str: string) → boolean
Skips to the start of the next occurrence of the given string, if found on the current line (doesn't advance the stream if the string does not occur on the line). Returns true if the string was found.
调到给定字符串的下一次出现的开头,如果在当前行上找到(如果该字符串没有在该行上出现,则不提前该stream)。 如果找到字符串,返回true.
match(pattern: string, ?consume: boolean, ?caseFold: boolean) → boolean
match(pattern: regexp, ?consume: boolean) → array<string>
Act like a multi-character eat—if consume is true or not given—or a look-ahead that doesn't update the stream position—if it is false. pattern can be either a string or a regular expression starting with ^. When it is a string, caseFold can be set to true to make the match case-insensitive. When successfully matching a regular expression, the returned value will be the array returned by match, in case you need to extract matched groups.
就像一个多字符的 eat-如果consume为true或者没有给出--或者如果错误,不更新stream的位置。 pattern 可以是字符串,也可以是以 ^ 开头的正则表达式。当它是字符串时,可以将 caseFold设置为true, 使匹配不区分大小写。当成功匹配正则表达式时,返回的值将是通过match返回的数组,以防你需要提取匹配的组。
backUp(n: integer)
Backs up the stream n characters. Backing it up further than the start of the current token will cause things to break, so be careful.
备份stream n 个字符。比当前token的开头还要支持 它会导致事情中断,所以要注意使用。
column() → integer
Returns the column (taking into account tabs) at which the current token starts.
返回当前token开始的列(考虑标签)。
indentation() → integer
Tells you how far the current line has been indented, in spaces. Corrects for tab characters.
告诉你当前行在空格中缩进了多元, 校正tab 字符。
current() → string
Get the string between the start of the current token and the current stream position.
获取当前token开始与当前stream位置之间的字符串。
lookAhead(n: number) → ?string
Get the line n (>0) lines after the current one, in order to scan ahead across line boundaries. Note that you want to do this carefully, since looking far ahead will make mode state caching much less effective.
在当前行后获取第 n (n > 0) 行 ,为了跨越行边界向前查看。注意小心的执行此操作,因为查找太远会使mode状态缓存变得不那么有效。
baseToken() → ?{type: ?string, size: number}
Modes added through addOverlay (and only such modes) can use this method to inspect the current token produced by the underlying mode.
通过 addOverlay 添加 modes。(而且只有这种modes)才能使用这个方法来检查由底层mode产生的当前token。

By default, blank lines are simply skipped when tokenizing a document. For languages that have significant blank lines, you can define a blankLine(state) method on your mode that will get called whenever a blank line is passed over, so that it can update the parser state.

默认情况下,当标记文档时空白行可以轻易的略过。对于具有显著空白行的语言,你可以在你的mode上定义一个 blankLine(state) 方法,每当有空行被传递时,这个方法将被调用,以便它可以更新解析器的状态。

 

 

Because state object are mutated, and CodeMirror needs to keep valid versions of a state around so that it can restart a parse at any line, copies must be made of state objects. The default algorithm used is that a new state object is created, which gets all the properties of the old object. Any properties which hold arrays get a copy of these arrays (since arrays tend to be used as mutable stacks). When this is not correct, for example because a mode mutates non-array properties of its state object, a mode object should define a copyState method, which is given a state and should return a safe copy of that state.

因为状态对象是突变的,并且CodeMirror需要保持状态的有效版本,以便它可以在任何行重新启动解析,所以必须对state对象进行复制。所使用的默认算法是创建新的state对象,它获取旧对象的所有属性。任何持有数组的属性都会得到这个数组的拷贝(因为数组往往被用作可变栈)。当这是不正确时,例如,因为mode会改变其状态对象的非数组属性,一个mode对象应该定义一个 copyState方法,该方法被赋予一个状态,并且应该返回该状态的安全副本。

 

 

If you want your mode to provide smart indentation (through the indentLine method and the indentAuto and newlineAndIndent commands, to which keys can be bound), you must define an indent(state, textAfter) method on your mode object.

如果你希望你的mode提供智能缩进(通过 indentLine 方法和 indentAuto, 和 newlineAndIndent命令,可以将键绑定到他们), 你必须定义一个 indent(state, textAfter)方法在你的mode对象中。

 

The indentation method should inspect the given state object, and optionally the textAfter string, which contains the text on the line that is being indented, and return an integer, the amount of spaces to indent. It should usually take the indentUnit option into account. An indentation method may return CodeMirror.Pass to indicate that it could not come up with a precise indentation.

缩进方法应该检查给定的状态对象, 以及可选的 textAfter 字符串, 其中包含要缩进的行上的文本,并且返回一个整数,即要缩进的空格数量。通常应该考虑 indentUnit 选项。一个缩进方法可以返回 CodeMirror.pass 表示不能产生精确额缩进。

 

 

To work well with the commenting addon, a mode may define lineComment (string that starts a line comment), blockCommentStartblockCommentEnd (strings that start and end block comments), and blockCommentLead (a string to put at the start of continued lines in a block comment). All of these are optional.

为了更好的使用 commenting addon(注释插件),mode可以定义 lineComment(行注释), blockCommentStart, blockCommentEnd(块注释), 和 blockCommentLead(在块注释中连续行的注释)。这些都是可选的

 

Finally, a mode may define either an electricChars or an electricInput property, which are used to automatically reindent the line when certain patterns are typed and the electricChars option is enabled. electricChars may be a string, and will trigger a reindent whenever one of the characters in that string are typed. Often, it is more appropriate to use electricInput, which should hold a regular expression, and will trigger indentation when the part of the line before the cursor matches the expression. It should usually end with a $ character, so that it only matches when the indentation-changing pattern was just typed, not when something was typed after the pattern.

最后, mode可以定义一个 electricChars 或者 electricInput 属性, 当输入某些模式并且启用 electricInput选项时,这些属性用于自动重写行。electricChars 可以是一个字符串, 当该字符串中的某一个字符被键入时, 它将触发一个reindent(换行?)。 通常使用 electricInput 更合适,它应该是一个正则表达式,并且当光标之前的行的部分匹配表达式时将触发缩进。它通常以 $ 结尾, 因此只有在缩进更改模式刚键入时才匹配,而不是在模式之后当键入某些东西时才匹配。

 

So, to summarize, a mode must provide a token method, and it may provide startStatecopyState, and indent methods. For an example of a trivial mode, see the diff mode, for a more involved example, see the C-like mode.

因此,总结一下,一个mode必须提供一个 token 方法, 并且它可以提供 startState, copyState 和 indent 方法。 一个平凡mode的例子,可以参考diff mode,  对于一个更为复杂的例子,可以参考 C-like mode.

 

 

Sometimes, it is useful for modes to nest—to have one mode delegate work to another mode. An example of this kind of mode is the mixed-mode HTML mode. To implement such nesting, it is usually necessary to create mode objects and copy states yourself. To create a mode object, there are CodeMirror.getMode(options, parserConfig), where the first argument is a configuration object as passed to the mode constructor function, and the second argument is a mode specification as in the mode option. To copy a state object, call CodeMirror.copyState(mode, state), where mode is the mode that created the given state.

有时, modes嵌套可以使一个mode委托工作到另一个mode。这种mode的一个例子是mixed-mode HTML mode。为了实现这种嵌套,通常需要自己创建mode对象和复制状态。创建mode对象, 这里有 CodeMirror.getMode(options, parserConfig), 其中第一个参数是传递给mode构造函数时的配置对象,第二个参数是mode选项中的mode规范。要复制一个state对象,调用 CodeMirror.copyState(mode, state), 其中 mode 是创建给定state的mode。

 

 

In a nested mode, it is recommended to add an extra method, innerMode which, given a state object, returns a {state, mode} object with the inner mode and its state for the current position. These are used by utility scripts such as the tag closer to get context information. Use the CodeMirror.innerMode helper function to, starting from a mode and a state, recursively walk down to the innermost mode and state.

在嵌套模式中,建议添加一个额外的方法, innerMode 给定一个state对象,它返回一个具有内部mode的{state,mode} 对象,以及当前位置的state。这些工具有诸如 tag closer之类的实用脚本使用,以获得上下文信息。使用 CodeMirror.innerMode帮助函数,从一个mode和一个state开始,递归的向下走到最内部的mode和state。

 

To make indentation work properly in a nested parser, it is advisable to give the startState method of modes that are intended to be nested an optional argument that provides the base indentation for the block of code. The JavaScript and CSS parser do this, for example, to allow JavaScript and CSS code inside the mixed-mode HTML mode to be properly indented.

为了使缩进在嵌套解析器中正常工作,建议为要嵌套的mode 的 startState 方法提供一个可选的参数,为代码块提供基本缩进。例如, javascript 和 css 解析器这样做事为了允许 mixed-mode HTML mode 中的javascript和css代码适当缩进。

 

 

It is possible, and encouraged, to associate your mode, or a certain configuration of your mode, with a MIME type. For example, the JavaScript mode associates itself with text/javascript, and its JSON variant with application/json. To do this, call CodeMirror.defineMIME(mime, modeSpec), where modeSpec can be a string or object specifying a mode, as in the mode option.

有可能,并鼓励,将你的mode或者你的mode的某种配置和 MIME 类型联系起来。例如,javascript mode 本身与 text/javascript 关联,它的JSON变体 application/json。为此,调用 CodeMirror.defineMIME(mime, modeSpec),其中 modeSpec 可以是指定mode的字符串或对象,如mode选项。

 

If a mode specification wants to add some properties to the resulting mode object, typically for use with getHelpers, it may contain a modeProps property, which holds an object. This object's properties will be copied to the actual mode object.

如果mode规范希望向结果 mode对象添加一些属性,通常使用getHelpers, 那么它可能包含一个 modeProps属性, 其中包含一个对象。该对象的属性将被复制到实际的mode对象。

 

 

Sometimes, it is useful to add or override mode object properties from external code. The CodeMirror.extendMode function can be used to add properties to mode objects produced for a specific mode. Its first argument is the name of the mode, its second an object that specifies the properties that should be added. This is mostly useful to add utilities that can later be looked up through getMode.

有时,从外部代码添加或重写mode对象属性是有用的。CodeMirror.extendMode函数可用于为特定mode生成的mode对象添加属性。它的第一个参数是模式的名称,第二个参数是指定应该添加的属性的对象。这可以通过getMode添加可以稍后查找的实用工具非常有用。

 

posted @ 2018-11-14 16:56  她在村口等我  阅读(745)  评论(0编辑  收藏  举报