@yozora/tokenizer-indented-code 2.0.3 → 2.0.5-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/{index.js → index.cjs} +5 -6
- package/lib/esm/{index.js → index.mjs} +4 -5
- package/lib/types/index.d.ts +4 -4
- package/package.json +18 -14
- package/src/index.ts +9 -0
- package/src/match.ts +94 -0
- package/src/parse.ts +54 -0
- package/src/tokenizer.ts +31 -0
- package/src/types.ts +22 -0
|
@@ -103,18 +103,17 @@ const uniqueName = '@yozora/tokenizer-indented-code';
|
|
|
103
103
|
|
|
104
104
|
class IndentedCodeTokenizer extends coreTokenizer.BaseBlockTokenizer {
|
|
105
105
|
constructor(props = {}) {
|
|
106
|
-
var _a, _b;
|
|
107
106
|
super({
|
|
108
|
-
name:
|
|
109
|
-
priority:
|
|
107
|
+
name: props.name ?? uniqueName,
|
|
108
|
+
priority: props.priority ?? coreTokenizer.TokenizerPriority.ATOMIC,
|
|
110
109
|
});
|
|
111
|
-
this.match = match;
|
|
112
|
-
this.parse = parse;
|
|
113
110
|
}
|
|
111
|
+
match = match;
|
|
112
|
+
parse = parse;
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
exports.IndentedCodeTokenizer = IndentedCodeTokenizer;
|
|
117
116
|
exports.IndentedCodeTokenizerName = uniqueName;
|
|
118
|
-
exports
|
|
117
|
+
exports.default = IndentedCodeTokenizer;
|
|
119
118
|
exports.indentedCodeMatch = match;
|
|
120
119
|
exports.indentedCodeParse = parse;
|
|
@@ -99,14 +99,13 @@ const uniqueName = '@yozora/tokenizer-indented-code';
|
|
|
99
99
|
|
|
100
100
|
class IndentedCodeTokenizer extends BaseBlockTokenizer {
|
|
101
101
|
constructor(props = {}) {
|
|
102
|
-
var _a, _b;
|
|
103
102
|
super({
|
|
104
|
-
name:
|
|
105
|
-
priority:
|
|
103
|
+
name: props.name ?? uniqueName,
|
|
104
|
+
priority: props.priority ?? TokenizerPriority.ATOMIC,
|
|
106
105
|
});
|
|
107
|
-
this.match = match;
|
|
108
|
-
this.parse = parse;
|
|
109
106
|
}
|
|
107
|
+
match = match;
|
|
108
|
+
parse = parse;
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
export { IndentedCodeTokenizer, uniqueName as IndentedCodeTokenizerName, IndentedCodeTokenizer as default, match as indentedCodeMatch, parse as indentedCodeParse };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { IPartialYastBlockToken, IPhrasingContentLine, ITokenizer, IBaseBlockTokenizerProps, IMatchBlockHookCreator, IParseBlockHookCreator, BaseBlockTokenizer, IBlockTokenizer } from '@yozora/core-tokenizer';
|
|
2
2
|
import { CodeType, Code } from '@yozora/ast';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
type T = CodeType;
|
|
5
|
+
type INode = Code;
|
|
6
6
|
declare const uniqueName = "@yozora/tokenizer-indented-code";
|
|
7
7
|
interface IToken extends IPartialYastBlockToken<T> {
|
|
8
8
|
/**
|
|
@@ -10,8 +10,8 @@ interface IToken extends IPartialYastBlockToken<T> {
|
|
|
10
10
|
*/
|
|
11
11
|
lines: IPhrasingContentLine[];
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
type IThis = ITokenizer;
|
|
14
|
+
type ITokenizerProps = Partial<IBaseBlockTokenizerProps>;
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* An indented code block is composed of one or more indented chunks
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yozora/tokenizer-indented-code",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5-alpha.0",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "guanghechen",
|
|
6
6
|
"url": "https://github.com/guanghechen/"
|
|
@@ -11,33 +11,37 @@
|
|
|
11
11
|
"directory": "tokenizers/indented-code"
|
|
12
12
|
},
|
|
13
13
|
"homepage": "https://github.com/yozorajs/yozora/tree/release-2.x.x/tokenizers/indented-code",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
"type": "module",
|
|
15
|
+
"exports": {
|
|
16
|
+
"types": "./lib/types/index.d.ts",
|
|
17
|
+
"import": "./lib/esm/index.mjs",
|
|
18
|
+
"require": "./lib/cjs/index.cjs"
|
|
19
|
+
},
|
|
20
|
+
"source": "./src/index.ts",
|
|
21
|
+
"types": "./lib/types/index.d.ts",
|
|
22
|
+
"main": "./lib/cjs/index.cjs",
|
|
23
|
+
"module": "./lib/esm/index.mjs",
|
|
18
24
|
"license": "MIT",
|
|
19
25
|
"engines": {
|
|
20
26
|
"node": ">= 16.0.0"
|
|
21
27
|
},
|
|
22
28
|
"files": [
|
|
23
29
|
"lib/",
|
|
24
|
-
"
|
|
25
|
-
"!lib/**/*.d.ts.map",
|
|
30
|
+
"src/",
|
|
26
31
|
"package.json",
|
|
27
32
|
"CHANGELOG.md",
|
|
28
33
|
"LICENSE",
|
|
29
34
|
"README.md"
|
|
30
35
|
],
|
|
31
36
|
"scripts": {
|
|
32
|
-
"build": "cross-env NODE_ENV=production rollup -c ../../rollup.config.
|
|
33
|
-
"prebuild": "rimraf lib/",
|
|
37
|
+
"build": "rimraf lib/ && cross-env NODE_ENV=production rollup -c ../../rollup.config.mjs",
|
|
34
38
|
"prepublishOnly": "cross-env ROLLUP_SHOULD_SOURCEMAP=false yarn build",
|
|
35
|
-
"test": "cross-env TS_NODE_FILES=true jest --config ../../jest.config.
|
|
39
|
+
"test": "cross-env TS_NODE_FILES=true NODE_OPTIONS=--experimental-vm-modules jest --config ../../jest.config.mjs --rootDir ."
|
|
36
40
|
},
|
|
37
41
|
"dependencies": {
|
|
38
|
-
"@yozora/ast": "^2.0.
|
|
39
|
-
"@yozora/character": "^2.0.
|
|
40
|
-
"@yozora/core-tokenizer": "^2.0.
|
|
42
|
+
"@yozora/ast": "^2.0.5-alpha.0",
|
|
43
|
+
"@yozora/character": "^2.0.5-alpha.0",
|
|
44
|
+
"@yozora/core-tokenizer": "^2.0.5-alpha.0"
|
|
41
45
|
},
|
|
42
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "8bf941fe4ef82947165b0f3cc123cd493665e13b"
|
|
43
47
|
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { match as indentedCodeMatch } from './match'
|
|
2
|
+
export { parse as indentedCodeParse } from './parse'
|
|
3
|
+
export { IndentedCodeTokenizer, IndentedCodeTokenizer as default } from './tokenizer'
|
|
4
|
+
export { uniqueName as IndentedCodeTokenizerName } from './types'
|
|
5
|
+
export type {
|
|
6
|
+
IThis as IIndentedCodeHookContext,
|
|
7
|
+
IToken as IIndentedCodeToken,
|
|
8
|
+
ITokenizerProps as IIndentedCodeTokenizerProps,
|
|
9
|
+
} from './types'
|
package/src/match.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { CodeType } from '@yozora/ast'
|
|
2
|
+
import { AsciiCodePoint, VirtualCodePoint } from '@yozora/character'
|
|
3
|
+
import type {
|
|
4
|
+
IMatchBlockHookCreator,
|
|
5
|
+
IPhrasingContentLine,
|
|
6
|
+
IResultOfEatContinuationText,
|
|
7
|
+
IResultOfEatOpener,
|
|
8
|
+
} from '@yozora/core-tokenizer'
|
|
9
|
+
import { calcEndPoint, calcStartPoint } from '@yozora/core-tokenizer'
|
|
10
|
+
import type { IThis, IToken, T } from './types'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* An indented code block is composed of one or more indented chunks
|
|
14
|
+
* separated by blank lines. An indented chunk is a sequence of non-blank
|
|
15
|
+
* lines, each indented four or more spaces. The contents of the code block
|
|
16
|
+
* are the literal contents of the lines, including trailing line endings,
|
|
17
|
+
* minus four spaces of indentation.
|
|
18
|
+
*
|
|
19
|
+
* @see https://github.github.com/gfm/#indented-code-block
|
|
20
|
+
*/
|
|
21
|
+
export const match: IMatchBlockHookCreator<T, IToken, IThis> = function () {
|
|
22
|
+
return {
|
|
23
|
+
isContainingBlock: false,
|
|
24
|
+
eatOpener,
|
|
25
|
+
eatContinuationText,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function eatOpener(line: Readonly<IPhrasingContentLine>): IResultOfEatOpener<T, IToken> {
|
|
29
|
+
if (line.countOfPrecedeSpaces < 4) return null
|
|
30
|
+
const { nodePoints, startIndex, firstNonWhitespaceIndex, endIndex } = line
|
|
31
|
+
|
|
32
|
+
let firstIndex = startIndex + 4
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* If there exists 1-3 spaces before a tab forms the indent, the remain
|
|
36
|
+
* virtual spaces of the tab should not be a part of the contents.
|
|
37
|
+
* @see https://github.github.com/gfm/#example-2
|
|
38
|
+
*/
|
|
39
|
+
if (
|
|
40
|
+
nodePoints[startIndex].codePoint === AsciiCodePoint.SPACE &&
|
|
41
|
+
nodePoints[startIndex + 3].codePoint === VirtualCodePoint.SPACE
|
|
42
|
+
) {
|
|
43
|
+
let i = startIndex + 1
|
|
44
|
+
for (; i < firstNonWhitespaceIndex; ++i) {
|
|
45
|
+
if (nodePoints[i].codePoint === VirtualCodePoint.SPACE) break
|
|
46
|
+
}
|
|
47
|
+
firstIndex = i + 4
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const nextIndex = endIndex
|
|
51
|
+
const token: IToken = {
|
|
52
|
+
nodeType: CodeType,
|
|
53
|
+
position: {
|
|
54
|
+
start: calcStartPoint(nodePoints, startIndex),
|
|
55
|
+
end: calcEndPoint(nodePoints, nextIndex - 1),
|
|
56
|
+
},
|
|
57
|
+
lines: [
|
|
58
|
+
{
|
|
59
|
+
nodePoints,
|
|
60
|
+
startIndex: firstIndex,
|
|
61
|
+
endIndex,
|
|
62
|
+
firstNonWhitespaceIndex,
|
|
63
|
+
countOfPrecedeSpaces: line.countOfPrecedeSpaces - (firstIndex - startIndex),
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
}
|
|
67
|
+
return { token, nextIndex }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function eatContinuationText(
|
|
71
|
+
line: Readonly<IPhrasingContentLine>,
|
|
72
|
+
token: IToken,
|
|
73
|
+
): IResultOfEatContinuationText {
|
|
74
|
+
const { nodePoints, startIndex, endIndex, firstNonWhitespaceIndex, countOfPrecedeSpaces } = line
|
|
75
|
+
|
|
76
|
+
if (countOfPrecedeSpaces < 4 && firstNonWhitespaceIndex < endIndex)
|
|
77
|
+
return { status: 'notMatched' }
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Blank line is allowed
|
|
81
|
+
* @see https://github.github.com/gfm/#example-81
|
|
82
|
+
* @see https://github.github.com/gfm/#example-82
|
|
83
|
+
*/
|
|
84
|
+
const firstIndex = Math.min(endIndex - 1, startIndex + 4)
|
|
85
|
+
token.lines.push({
|
|
86
|
+
nodePoints,
|
|
87
|
+
startIndex: firstIndex,
|
|
88
|
+
endIndex,
|
|
89
|
+
firstNonWhitespaceIndex,
|
|
90
|
+
countOfPrecedeSpaces: countOfPrecedeSpaces - (firstIndex - startIndex),
|
|
91
|
+
})
|
|
92
|
+
return { status: 'opening', nextIndex: endIndex }
|
|
93
|
+
}
|
|
94
|
+
}
|
package/src/parse.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { CodeType } from '@yozora/ast'
|
|
2
|
+
import type { INodePoint } from '@yozora/character'
|
|
3
|
+
import { calcStringFromNodePoints } from '@yozora/character'
|
|
4
|
+
import type { IParseBlockHookCreator } from '@yozora/core-tokenizer'
|
|
5
|
+
import { mergeContentLinesFaithfully } from '@yozora/core-tokenizer'
|
|
6
|
+
import type { INode, IThis, IToken, T } from './types'
|
|
7
|
+
|
|
8
|
+
export const parse: IParseBlockHookCreator<T, IToken, INode, IThis> = function (api) {
|
|
9
|
+
return {
|
|
10
|
+
parse: tokens =>
|
|
11
|
+
tokens.map(token => {
|
|
12
|
+
/**
|
|
13
|
+
* Blank lines preceding or following an indented code block
|
|
14
|
+
* are not included in it
|
|
15
|
+
* @see https://github.github.com/gfm/#example-87
|
|
16
|
+
*/
|
|
17
|
+
const { lines } = token
|
|
18
|
+
let startLineIndex = 0,
|
|
19
|
+
endLineIndex = lines.length
|
|
20
|
+
for (; startLineIndex < endLineIndex; ++startLineIndex) {
|
|
21
|
+
const line = lines[startLineIndex]
|
|
22
|
+
if (line.firstNonWhitespaceIndex < line.endIndex) break
|
|
23
|
+
}
|
|
24
|
+
for (; startLineIndex < endLineIndex; --endLineIndex) {
|
|
25
|
+
const line = lines[endLineIndex - 1]
|
|
26
|
+
if (line.firstNonWhitespaceIndex < line.endIndex) break
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const contents: INodePoint[] = mergeContentLinesFaithfully(
|
|
30
|
+
lines,
|
|
31
|
+
startLineIndex,
|
|
32
|
+
endLineIndex,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
let value: string = calcStringFromNodePoints(contents)
|
|
36
|
+
if (!/\n$/.test(value)) value += '\n'
|
|
37
|
+
const node: INode = api.shouldReservePosition
|
|
38
|
+
? {
|
|
39
|
+
type: CodeType,
|
|
40
|
+
position: token.position,
|
|
41
|
+
lang: null,
|
|
42
|
+
meta: null,
|
|
43
|
+
value,
|
|
44
|
+
}
|
|
45
|
+
: {
|
|
46
|
+
type: CodeType,
|
|
47
|
+
lang: null,
|
|
48
|
+
meta: null,
|
|
49
|
+
value,
|
|
50
|
+
}
|
|
51
|
+
return node
|
|
52
|
+
}),
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/tokenizer.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IBlockTokenizer,
|
|
3
|
+
IMatchBlockHookCreator,
|
|
4
|
+
IParseBlockHookCreator,
|
|
5
|
+
} from '@yozora/core-tokenizer'
|
|
6
|
+
import { BaseBlockTokenizer, TokenizerPriority } from '@yozora/core-tokenizer'
|
|
7
|
+
import { match } from './match'
|
|
8
|
+
import { parse } from './parse'
|
|
9
|
+
import type { INode, IThis, IToken, ITokenizerProps, T } from './types'
|
|
10
|
+
import { uniqueName } from './types'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Lexical Analyzer for IndentedCode.
|
|
14
|
+
* @see https://github.github.com/gfm/#indented-code-block
|
|
15
|
+
*/
|
|
16
|
+
export class IndentedCodeTokenizer
|
|
17
|
+
extends BaseBlockTokenizer<T, IToken, INode, IThis>
|
|
18
|
+
implements IBlockTokenizer<T, IToken, INode, IThis>
|
|
19
|
+
{
|
|
20
|
+
/* istanbul ignore next */
|
|
21
|
+
constructor(props: ITokenizerProps = {}) {
|
|
22
|
+
super({
|
|
23
|
+
name: props.name ?? uniqueName,
|
|
24
|
+
priority: props.priority ?? TokenizerPriority.ATOMIC,
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public override readonly match: IMatchBlockHookCreator<T, IToken, IThis> = match
|
|
29
|
+
|
|
30
|
+
public override readonly parse: IParseBlockHookCreator<T, IToken, INode, IThis> = parse
|
|
31
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Code, CodeType } from '@yozora/ast'
|
|
2
|
+
import type {
|
|
3
|
+
IBaseBlockTokenizerProps,
|
|
4
|
+
IPartialYastBlockToken,
|
|
5
|
+
IPhrasingContentLine,
|
|
6
|
+
ITokenizer,
|
|
7
|
+
} from '@yozora/core-tokenizer'
|
|
8
|
+
|
|
9
|
+
export type T = CodeType
|
|
10
|
+
export type INode = Code
|
|
11
|
+
export const uniqueName = '@yozora/tokenizer-indented-code'
|
|
12
|
+
|
|
13
|
+
export interface IToken extends IPartialYastBlockToken<T> {
|
|
14
|
+
/**
|
|
15
|
+
* Lines to construct the contents of a paragraph.
|
|
16
|
+
*/
|
|
17
|
+
lines: IPhrasingContentLine[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type IThis = ITokenizer
|
|
21
|
+
|
|
22
|
+
export type ITokenizerProps = Partial<IBaseBlockTokenizerProps>
|