@hydranium/core 1.0.0-next.71 → 1.0.0-next.72
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.
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
|
-
import { type AstNode, type AstReflection, type LangiumDocument, type ReferenceInfo } from '@hydranium/langium';
|
|
9
|
+
import { type AstNode, type AstReflection, type LangiumDocument, type LeafCstNode, type ReferenceInfo } from '@hydranium/langium';
|
|
10
10
|
import { AbstractSemanticTokenProvider, type SemanticTokenAcceptor } from '@hydranium/langium/lsp';
|
|
11
11
|
import { type CancellationToken } from 'vscode-languageserver';
|
|
12
12
|
import { type NameProvider } from '../langium/naming/name-provider.js';
|
|
@@ -37,6 +37,22 @@ export interface HydraniumSemanticTokenProviderOptions {
|
|
|
37
37
|
* render in the editor's default foreground.
|
|
38
38
|
*/
|
|
39
39
|
readonly highlightKeywords?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Also emit a `comment` token for every comment leaf of the CST.
|
|
42
|
+
*
|
|
43
|
+
* OFF by default for the same reason as {@link highlightKeywords}, and the
|
|
44
|
+
* loss is larger: a TextMate grammar distinguishes `comment.line` from
|
|
45
|
+
* `comment.block` and scopes the delimiters separately, where a semantic
|
|
46
|
+
* token carries one flat `comment` that overrides all of them. It exists
|
|
47
|
+
* for the host that ships no client-side grammar, where a comment otherwise
|
|
48
|
+
* renders in the editor's default foreground.
|
|
49
|
+
*
|
|
50
|
+
* What counts as a comment is every hidden terminal the lexer retains,
|
|
51
|
+
* which is every hidden terminal whose pattern is not whitespace. A grammar
|
|
52
|
+
* whose hidden non-whitespace terminal means something other than a comment
|
|
53
|
+
* is miscoloured, and this option is the place to say no.
|
|
54
|
+
*/
|
|
55
|
+
readonly highlightComments?: boolean;
|
|
40
56
|
}
|
|
41
57
|
/**
|
|
42
58
|
* Semantic-token provider that derives highlighting from ONE adopter-supplied
|
|
@@ -102,6 +118,23 @@ export declare abstract class AbstractHydraniumSemanticTokenProvider extends Abs
|
|
|
102
118
|
* drops anything outside `currentRange` on its own.
|
|
103
119
|
*/
|
|
104
120
|
protected computeHighlighting(document: LangiumDocument, acceptor: SemanticTokenAcceptor, cancelToken: CancellationToken): Promise<void>;
|
|
121
|
+
/**
|
|
122
|
+
* Emit a comment as ONE TOKEN PER LINE, never as one multi-line range.
|
|
123
|
+
*
|
|
124
|
+
* A block comment is the first token this provider emits that can span
|
|
125
|
+
* lines, and the upstream encoder mishandles that for a client which does
|
|
126
|
+
* not advertise `multilineTokenSupport` — the split path computes the first
|
|
127
|
+
* line's length as `nextLineOffset - range.start.character - 1`, subtracting
|
|
128
|
+
* a COLUMN from a document OFFSET, so the length is right only for a token
|
|
129
|
+
* starting on line 0 and overruns its line everywhere else. No client in
|
|
130
|
+
* this repository advertises that capability, so the broken path is the one
|
|
131
|
+
* taken. Splitting here means every range handed over starts and ends on one
|
|
132
|
+
* line and the arithmetic is never reached.
|
|
133
|
+
*
|
|
134
|
+
* Lines come from the leaf's own text rather than from the document, so
|
|
135
|
+
* nothing has to re-derive where the comment ends.
|
|
136
|
+
*/
|
|
137
|
+
protected acceptComment(cst: LeafCstNode, acceptor: SemanticTokenAcceptor): void;
|
|
105
138
|
protected highlightElement(node: AstNode, acceptor: SemanticTokenAcceptor): void | undefined | 'prune';
|
|
106
139
|
/** Colour the node's own name, at whichever property the NameProvider reads it from. */
|
|
107
140
|
protected highlightDeclaration(node: AstNode, acceptor: SemanticTokenAcceptor): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semantic-token-provider.d.ts","sourceRoot":"","sources":["../../src/lsp/semantic-token-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EACJ,KAAK,OAAO,EACZ,KAAK,aAAa,
|
|
1
|
+
{"version":3,"file":"semantic-token-provider.d.ts","sourceRoot":"","sources":["../../src/lsp/semantic-token-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EACJ,KAAK,OAAO,EACZ,KAAK,aAAa,EAMlB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,aAAa,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,6BAA6B,EAAE,KAAK,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AACnG,OAAO,EAAE,KAAK,iBAAiB,EAA8C,MAAM,uBAAuB,CAAC;AAC3G,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAE5E;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAAE,CAAC;AAU1G,6EAA6E;AAC7E,MAAM,WAAW,qCAAqC;IACnD;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAErC;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,8BAAsB,sCAAuC,SAAQ,6BAA6B;IAC/F,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IAC9C,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;IAC7C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,qCAAqC,CAAC;IAElE;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAsC;gBAEpF,QAAQ,EAAE,sBAAsB,EAAE,OAAO,GAAE,qCAA0C;IAOjG;;;;;;;;;;;;;;;OAeG;cACsB,mBAAmB,CACzC,QAAQ,EAAE,eAAe,EACzB,QAAQ,EAAE,qBAAqB,EAC/B,WAAW,EAAE,iBAAiB,GAC9B,OAAO,CAAC,IAAI,CAAC;IAoChB;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,qBAAqB,GAAG,IAAI;cAkB7D,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,qBAAqB,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO;IAM/G,wFAAwF;IACxF,SAAS,CAAC,oBAAoB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,qBAAqB,GAAG,IAAI;IAYpF,kFAAkF;IAClF,SAAS,CAAC,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,qBAAqB,GAAG,IAAI;IAUnF;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAE5E,0FAA0F;IAC1F,SAAS,CAAC,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,iBAAiB,GAAG,SAAS;IAI1E,4GAA4G;IAC5G,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,iBAAiB,GAAG,SAAS;IAI9E,uFAAuF;IACvF,SAAS,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS;CAG9D"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
|
-
import { AstUtils, CstUtils, GrammarAST, interruptAndCheck } from '@hydranium/langium';
|
|
9
|
+
import { AstUtils, CstUtils, GrammarAST, interruptAndCheck, isLeafCstNode } from '@hydranium/langium';
|
|
10
10
|
import { AbstractSemanticTokenProvider } from '@hydranium/langium/lsp';
|
|
11
11
|
import { SemanticTokenModifiers, SemanticTokenTypes } from 'vscode-languageserver';
|
|
12
12
|
/** Normalise both {@link SemanticTokenKind} forms to the acceptor's shape. */
|
|
@@ -86,7 +86,9 @@ export class AbstractHydraniumSemanticTokenProvider extends AbstractSemanticToke
|
|
|
86
86
|
*/
|
|
87
87
|
async computeHighlighting(document, acceptor, cancelToken) {
|
|
88
88
|
await super.computeHighlighting(document, acceptor, cancelToken);
|
|
89
|
-
|
|
89
|
+
const keywords = this.options.highlightKeywords === true;
|
|
90
|
+
const comments = this.options.highlightComments === true;
|
|
91
|
+
if (!keywords && !comments) {
|
|
90
92
|
return;
|
|
91
93
|
}
|
|
92
94
|
const root = document.parseResult.value.$cstNode;
|
|
@@ -95,14 +97,59 @@ export class AbstractHydraniumSemanticTokenProvider extends AbstractSemanticToke
|
|
|
95
97
|
}
|
|
96
98
|
for (const cst of CstUtils.streamCst(root)) {
|
|
97
99
|
await interruptAndCheck(cancelToken);
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
// neither a composite-node check nor a hidden-token check adds
|
|
102
|
-
// anything here.
|
|
103
|
-
if (GrammarAST.isKeyword(cst.grammarSource)) {
|
|
100
|
+
// A keyword is always a leaf with a `Keyword` grammar source, so that
|
|
101
|
+
// check alone separates it from everything else in the walk.
|
|
102
|
+
if (keywords && GrammarAST.isKeyword(cst.grammarSource)) {
|
|
104
103
|
acceptor({ cst, type: SemanticTokenTypes.keyword });
|
|
105
104
|
}
|
|
105
|
+
// A comment leaf cannot be recognised the same way — the CST builder
|
|
106
|
+
// adds hidden nodes from the token alone, so `grammarSource` is
|
|
107
|
+
// undefined, which is also why the keyword filter never matches one.
|
|
108
|
+
//
|
|
109
|
+
// `hidden` alone IS the comment test, and nothing narrower is needed:
|
|
110
|
+
// the token builder groups a hidden terminal as `Lexer.SKIPPED` when
|
|
111
|
+
// its pattern matches whitespace and as `hidden` otherwise, so the
|
|
112
|
+
// lexer discards whitespace before a CST node is ever built and every
|
|
113
|
+
// hidden leaf that survives is a comment. Classifying the terminal
|
|
114
|
+
// again here would be a second guard for the same property, which no
|
|
115
|
+
// test could then distinguish. An adopter whose token builder RETAINS
|
|
116
|
+
// whitespace breaks that equivalence and would colour its gaps.
|
|
117
|
+
if (comments && isLeafCstNode(cst) && cst.hidden) {
|
|
118
|
+
this.acceptComment(cst, acceptor);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Emit a comment as ONE TOKEN PER LINE, never as one multi-line range.
|
|
124
|
+
*
|
|
125
|
+
* A block comment is the first token this provider emits that can span
|
|
126
|
+
* lines, and the upstream encoder mishandles that for a client which does
|
|
127
|
+
* not advertise `multilineTokenSupport` — the split path computes the first
|
|
128
|
+
* line's length as `nextLineOffset - range.start.character - 1`, subtracting
|
|
129
|
+
* a COLUMN from a document OFFSET, so the length is right only for a token
|
|
130
|
+
* starting on line 0 and overruns its line everywhere else. No client in
|
|
131
|
+
* this repository advertises that capability, so the broken path is the one
|
|
132
|
+
* taken. Splitting here means every range handed over starts and ends on one
|
|
133
|
+
* line and the arithmetic is never reached.
|
|
134
|
+
*
|
|
135
|
+
* Lines come from the leaf's own text rather than from the document, so
|
|
136
|
+
* nothing has to re-derive where the comment ends.
|
|
137
|
+
*/
|
|
138
|
+
acceptComment(cst, acceptor) {
|
|
139
|
+
const lines = cst.text.split('\n');
|
|
140
|
+
for (let index = 0; index < lines.length; index++) {
|
|
141
|
+
// A `\r\n` document leaves the carriage return on the line's end;
|
|
142
|
+
// colouring it would extend the token past the visible text.
|
|
143
|
+
const length = lines[index].replace(/\r$/, '').length;
|
|
144
|
+
const character = index === 0 ? cst.range.start.character : 0;
|
|
145
|
+
if (length === 0) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const line = cst.range.start.line + index;
|
|
149
|
+
acceptor({
|
|
150
|
+
range: { start: { line, character }, end: { line, character: character + length } },
|
|
151
|
+
type: SemanticTokenTypes.comment
|
|
152
|
+
});
|
|
106
153
|
}
|
|
107
154
|
}
|
|
108
155
|
highlightElement(node, acceptor) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semantic-token-provider.js","sourceRoot":"","sources":["../../src/lsp/semantic-token-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAGJ,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,iBAAiB,
|
|
1
|
+
{"version":3,"file":"semantic-token-provider.js","sourceRoot":"","sources":["../../src/lsp/semantic-token-provider.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hydranium/core",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.72",
|
|
4
4
|
"description": "Foundational runtime of the hydranium framework: AST coordination layer (services, integrity, AST extension, serialization, multi-client documents) plus the LSP textual head at the /lsp subpath. Consumed by protocol-head packages (@hydranium/data-server, @hydranium/glsp-server).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hydranium",
|
|
@@ -104,8 +104,8 @@
|
|
|
104
104
|
"diff": "^5.2.0"
|
|
105
105
|
},
|
|
106
106
|
"devDependencies": {
|
|
107
|
-
"@hydranium/langium": "1.0.0-next.
|
|
108
|
-
"@hydranium/protocol": "1.0.0-next.
|
|
107
|
+
"@hydranium/langium": "1.0.0-next.72",
|
|
108
|
+
"@hydranium/protocol": "1.0.0-next.72",
|
|
109
109
|
"@playwright/test": "^1.40.0",
|
|
110
110
|
"@types/diff": "^5.2.0",
|
|
111
111
|
"rimraf": "^5.0.0",
|
|
@@ -117,8 +117,8 @@
|
|
|
117
117
|
"vscode-languageserver-types": "^3.17.5"
|
|
118
118
|
},
|
|
119
119
|
"peerDependencies": {
|
|
120
|
-
"@hydranium/langium": "1.0.0-next.
|
|
121
|
-
"@hydranium/protocol": "1.0.0-next.
|
|
120
|
+
"@hydranium/langium": "1.0.0-next.72",
|
|
121
|
+
"@hydranium/protocol": "1.0.0-next.72",
|
|
122
122
|
"@playwright/test": "^1.40.0",
|
|
123
123
|
"vscode-jsonrpc": "9.0.1",
|
|
124
124
|
"vscode-languageserver": "~10.0.1",
|
|
@@ -14,7 +14,9 @@ import {
|
|
|
14
14
|
CstUtils,
|
|
15
15
|
GrammarAST,
|
|
16
16
|
interruptAndCheck,
|
|
17
|
+
isLeafCstNode,
|
|
17
18
|
type LangiumDocument,
|
|
19
|
+
type LeafCstNode,
|
|
18
20
|
type ReferenceInfo
|
|
19
21
|
} from '@hydranium/langium';
|
|
20
22
|
import { AbstractSemanticTokenProvider, type SemanticTokenAcceptor } from '@hydranium/langium/lsp';
|
|
@@ -54,6 +56,23 @@ export interface HydraniumSemanticTokenProviderOptions {
|
|
|
54
56
|
* render in the editor's default foreground.
|
|
55
57
|
*/
|
|
56
58
|
readonly highlightKeywords?: boolean;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Also emit a `comment` token for every comment leaf of the CST.
|
|
62
|
+
*
|
|
63
|
+
* OFF by default for the same reason as {@link highlightKeywords}, and the
|
|
64
|
+
* loss is larger: a TextMate grammar distinguishes `comment.line` from
|
|
65
|
+
* `comment.block` and scopes the delimiters separately, where a semantic
|
|
66
|
+
* token carries one flat `comment` that overrides all of them. It exists
|
|
67
|
+
* for the host that ships no client-side grammar, where a comment otherwise
|
|
68
|
+
* renders in the editor's default foreground.
|
|
69
|
+
*
|
|
70
|
+
* What counts as a comment is every hidden terminal the lexer retains,
|
|
71
|
+
* which is every hidden terminal whose pattern is not whitespace. A grammar
|
|
72
|
+
* whose hidden non-whitespace terminal means something other than a comment
|
|
73
|
+
* is miscoloured, and this option is the place to say no.
|
|
74
|
+
*/
|
|
75
|
+
readonly highlightComments?: boolean;
|
|
57
76
|
}
|
|
58
77
|
|
|
59
78
|
/**
|
|
@@ -133,7 +152,9 @@ export abstract class AbstractHydraniumSemanticTokenProvider extends AbstractSem
|
|
|
133
152
|
cancelToken: CancellationToken
|
|
134
153
|
): Promise<void> {
|
|
135
154
|
await super.computeHighlighting(document, acceptor, cancelToken);
|
|
136
|
-
|
|
155
|
+
const keywords = this.options.highlightKeywords === true;
|
|
156
|
+
const comments = this.options.highlightComments === true;
|
|
157
|
+
if (!keywords && !comments) {
|
|
137
158
|
return;
|
|
138
159
|
}
|
|
139
160
|
const root = document.parseResult.value.$cstNode;
|
|
@@ -142,14 +163,60 @@ export abstract class AbstractHydraniumSemanticTokenProvider extends AbstractSem
|
|
|
142
163
|
}
|
|
143
164
|
for (const cst of CstUtils.streamCst(root)) {
|
|
144
165
|
await interruptAndCheck(cancelToken);
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
// neither a composite-node check nor a hidden-token check adds
|
|
149
|
-
// anything here.
|
|
150
|
-
if (GrammarAST.isKeyword(cst.grammarSource)) {
|
|
166
|
+
// A keyword is always a leaf with a `Keyword` grammar source, so that
|
|
167
|
+
// check alone separates it from everything else in the walk.
|
|
168
|
+
if (keywords && GrammarAST.isKeyword(cst.grammarSource)) {
|
|
151
169
|
acceptor({ cst, type: SemanticTokenTypes.keyword });
|
|
152
170
|
}
|
|
171
|
+
// A comment leaf cannot be recognised the same way — the CST builder
|
|
172
|
+
// adds hidden nodes from the token alone, so `grammarSource` is
|
|
173
|
+
// undefined, which is also why the keyword filter never matches one.
|
|
174
|
+
//
|
|
175
|
+
// `hidden` alone IS the comment test, and nothing narrower is needed:
|
|
176
|
+
// the token builder groups a hidden terminal as `Lexer.SKIPPED` when
|
|
177
|
+
// its pattern matches whitespace and as `hidden` otherwise, so the
|
|
178
|
+
// lexer discards whitespace before a CST node is ever built and every
|
|
179
|
+
// hidden leaf that survives is a comment. Classifying the terminal
|
|
180
|
+
// again here would be a second guard for the same property, which no
|
|
181
|
+
// test could then distinguish. An adopter whose token builder RETAINS
|
|
182
|
+
// whitespace breaks that equivalence and would colour its gaps.
|
|
183
|
+
if (comments && isLeafCstNode(cst) && cst.hidden) {
|
|
184
|
+
this.acceptComment(cst, acceptor);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Emit a comment as ONE TOKEN PER LINE, never as one multi-line range.
|
|
191
|
+
*
|
|
192
|
+
* A block comment is the first token this provider emits that can span
|
|
193
|
+
* lines, and the upstream encoder mishandles that for a client which does
|
|
194
|
+
* not advertise `multilineTokenSupport` — the split path computes the first
|
|
195
|
+
* line's length as `nextLineOffset - range.start.character - 1`, subtracting
|
|
196
|
+
* a COLUMN from a document OFFSET, so the length is right only for a token
|
|
197
|
+
* starting on line 0 and overruns its line everywhere else. No client in
|
|
198
|
+
* this repository advertises that capability, so the broken path is the one
|
|
199
|
+
* taken. Splitting here means every range handed over starts and ends on one
|
|
200
|
+
* line and the arithmetic is never reached.
|
|
201
|
+
*
|
|
202
|
+
* Lines come from the leaf's own text rather than from the document, so
|
|
203
|
+
* nothing has to re-derive where the comment ends.
|
|
204
|
+
*/
|
|
205
|
+
protected acceptComment(cst: LeafCstNode, acceptor: SemanticTokenAcceptor): void {
|
|
206
|
+
const lines = cst.text.split('\n');
|
|
207
|
+
for (let index = 0; index < lines.length; index++) {
|
|
208
|
+
// A `\r\n` document leaves the carriage return on the line's end;
|
|
209
|
+
// colouring it would extend the token past the visible text.
|
|
210
|
+
const length = lines[index].replace(/\r$/, '').length;
|
|
211
|
+
const character = index === 0 ? cst.range.start.character : 0;
|
|
212
|
+
if (length === 0) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const line = cst.range.start.line + index;
|
|
216
|
+
acceptor({
|
|
217
|
+
range: { start: { line, character }, end: { line, character: character + length } },
|
|
218
|
+
type: SemanticTokenTypes.comment
|
|
219
|
+
});
|
|
153
220
|
}
|
|
154
221
|
}
|
|
155
222
|
|