diff options
author | Kristóf Marussy <marussy@mit.bme.hu> | 2021-11-16 17:09:58 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-16 17:09:58 +0100 |
commit | b4ba1dc1fec50a8114d49879dcb30891b8a8107e (patch) | |
tree | c40e4af57988becdcf7d363a2fb7107f668b8ba0 /language-web/src/main/js/xtext/xtextServiceResults.ts | |
parent | chore(lang): fix Sonar issue (diff) | |
parent | feat(web): xtext formatter client (diff) | |
download | refinery-b4ba1dc1fec50a8114d49879dcb30891b8a8107e.tar.gz refinery-b4ba1dc1fec50a8114d49879dcb30891b8a8107e.tar.zst refinery-b4ba1dc1fec50a8114d49879dcb30891b8a8107e.zip |
Merge pull request #13 from kris7t/xtext-formatter
Xtext formatter support
Diffstat (limited to 'language-web/src/main/js/xtext/xtextServiceResults.ts')
-rw-r--r-- | language-web/src/main/js/xtext/xtextServiceResults.ts | 287 |
1 files changed, 80 insertions, 207 deletions
diff --git a/language-web/src/main/js/xtext/xtextServiceResults.ts b/language-web/src/main/js/xtext/xtextServiceResults.ts index b2de1e4a..f79b059c 100644 --- a/language-web/src/main/js/xtext/xtextServiceResults.ts +++ b/language-web/src/main/js/xtext/xtextServiceResults.ts | |||
@@ -1,239 +1,112 @@ | |||
1 | export interface IPongResult { | 1 | import { z } from 'zod'; |
2 | pong: string; | ||
3 | } | ||
4 | |||
5 | export function isPongResult(result: unknown): result is IPongResult { | ||
6 | const pongResult = result as IPongResult; | ||
7 | return typeof pongResult === 'object' | ||
8 | && typeof pongResult.pong === 'string'; | ||
9 | } | ||
10 | |||
11 | export interface IDocumentStateResult { | ||
12 | stateId: string; | ||
13 | } | ||
14 | |||
15 | export function isDocumentStateResult(result: unknown): result is IDocumentStateResult { | ||
16 | const documentStateResult = result as IDocumentStateResult; | ||
17 | return typeof documentStateResult === 'object' | ||
18 | && typeof documentStateResult.stateId === 'string'; | ||
19 | } | ||
20 | |||
21 | export const VALID_CONFLICTS = ['invalidStateId', 'canceled'] as const; | ||
22 | |||
23 | export type Conflict = typeof VALID_CONFLICTS[number]; | ||
24 | |||
25 | export function isConflict(value: unknown): value is Conflict { | ||
26 | return typeof value === 'string' && VALID_CONFLICTS.includes(value as Conflict); | ||
27 | } | ||
28 | |||
29 | export interface IServiceConflictResult { | ||
30 | conflict: Conflict; | ||
31 | } | ||
32 | |||
33 | export function isServiceConflictResult(result: unknown): result is IServiceConflictResult { | ||
34 | const serviceConflictResult = result as IServiceConflictResult; | ||
35 | return typeof serviceConflictResult === 'object' | ||
36 | && isConflict(serviceConflictResult.conflict); | ||
37 | } | ||
38 | |||
39 | export function isInvalidStateIdConflictResult(result: unknown): boolean { | ||
40 | return isServiceConflictResult(result) && result.conflict === 'invalidStateId'; | ||
41 | } | ||
42 | |||
43 | export const VALID_SEVERITIES = ['error', 'warning', 'info', 'ignore'] as const; | ||
44 | |||
45 | export type Severity = typeof VALID_SEVERITIES[number]; | ||
46 | |||
47 | export function isSeverity(value: unknown): value is Severity { | ||
48 | return typeof value === 'string' && VALID_SEVERITIES.includes(value as Severity); | ||
49 | } | ||
50 | |||
51 | export interface IIssue { | ||
52 | description: string; | ||
53 | |||
54 | severity: Severity; | ||
55 | |||
56 | line: number; | ||
57 | |||
58 | column: number; | ||
59 | |||
60 | offset: number; | ||
61 | 2 | ||
62 | length: number; | 3 | export const pongResult = z.object({ |
63 | } | 4 | pong: z.string().nonempty(), |
5 | }); | ||
64 | 6 | ||
65 | export function isIssue(value: unknown): value is IIssue { | 7 | export type PongResult = z.infer<typeof pongResult>; |
66 | const issue = value as IIssue; | ||
67 | return typeof issue === 'object' | ||
68 | && typeof issue.description === 'string' | ||
69 | && isSeverity(issue.severity) | ||
70 | && typeof issue.line === 'number' | ||
71 | && typeof issue.column === 'number' | ||
72 | && typeof issue.offset === 'number' | ||
73 | && typeof issue.length === 'number'; | ||
74 | } | ||
75 | 8 | ||
76 | export interface IValidationResult { | 9 | export const documentStateResult = z.object({ |
77 | issues: IIssue[]; | 10 | stateId: z.string().nonempty(), |
78 | } | 11 | }); |
79 | 12 | ||
80 | function isArrayOfType<T>(value: unknown, check: (entry: unknown) => entry is T): value is T[] { | 13 | export type DocumentStateResult = z.infer<typeof documentStateResult>; |
81 | return Array.isArray(value) && (value as T[]).every(check); | ||
82 | } | ||
83 | 14 | ||
84 | export function isValidationResult(result: unknown): result is IValidationResult { | 15 | export const conflict = z.enum(['invalidStateId', 'canceled']); |
85 | const validationResult = result as IValidationResult; | ||
86 | return typeof validationResult === 'object' | ||
87 | && isArrayOfType(validationResult.issues, isIssue); | ||
88 | } | ||
89 | |||
90 | export interface IReplaceRegion { | ||
91 | offset: number; | ||
92 | 16 | ||
93 | length: number; | 17 | export type Conflict = z.infer<typeof conflict>; |
94 | 18 | ||
95 | text: string; | 19 | export const serviceConflictResult = z.object({ |
96 | } | 20 | conflict, |
21 | }); | ||
97 | 22 | ||
98 | export function isReplaceRegion(value: unknown): value is IReplaceRegion { | 23 | export type ServiceConflictResult = z.infer<typeof serviceConflictResult>; |
99 | const replaceRegion = value as IReplaceRegion; | ||
100 | return typeof replaceRegion === 'object' | ||
101 | && typeof replaceRegion.offset === 'number' | ||
102 | && typeof replaceRegion.length === 'number' | ||
103 | && typeof replaceRegion.text === 'string'; | ||
104 | } | ||
105 | |||
106 | export interface ITextRegion { | ||
107 | offset: number; | ||
108 | |||
109 | length: number; | ||
110 | } | ||
111 | |||
112 | export function isTextRegion(value: unknown): value is ITextRegion { | ||
113 | const textRegion = value as ITextRegion; | ||
114 | return typeof textRegion === 'object' | ||
115 | && typeof textRegion.offset === 'number' | ||
116 | && typeof textRegion.length === 'number'; | ||
117 | } | ||
118 | 24 | ||
119 | export const VALID_XTEXT_CONTENT_ASSIST_ENTRY_KINDS = [ | 25 | export function isConflictResult(result: unknown, conflictType: Conflict): boolean { |
120 | 'TEXT', | 26 | const parsedConflictResult = serviceConflictResult.safeParse(result); |
121 | 'METHOD', | 27 | return parsedConflictResult.success && parsedConflictResult.data.conflict === conflictType; |
122 | 'FUNCTION', | ||
123 | 'CONSTRUCTOR', | ||
124 | 'FIELD', | ||
125 | 'VARIABLE', | ||
126 | 'CLASS', | ||
127 | 'INTERFACE', | ||
128 | 'MODULE', | ||
129 | 'PROPERTY', | ||
130 | 'UNIT', | ||
131 | 'VALUE', | ||
132 | 'ENUM', | ||
133 | 'KEYWORD', | ||
134 | 'SNIPPET', | ||
135 | 'COLOR', | ||
136 | 'FILE', | ||
137 | 'REFERENCE', | ||
138 | 'UNKNOWN', | ||
139 | ] as const; | ||
140 | |||
141 | export type XtextContentAssistEntryKind = typeof VALID_XTEXT_CONTENT_ASSIST_ENTRY_KINDS[number]; | ||
142 | |||
143 | export function isXtextContentAssistEntryKind( | ||
144 | value: unknown, | ||
145 | ): value is XtextContentAssistEntryKind { | ||
146 | return typeof value === 'string' | ||
147 | && VALID_XTEXT_CONTENT_ASSIST_ENTRY_KINDS.includes(value as XtextContentAssistEntryKind); | ||
148 | } | 28 | } |
149 | 29 | ||
150 | export interface IContentAssistEntry { | 30 | export const severity = z.enum(['error', 'warning', 'info', 'ignore']); |
151 | prefix: string; | ||
152 | 31 | ||
153 | proposal: string; | 32 | export type Severity = z.infer<typeof severity>; |
154 | 33 | ||
155 | label?: string; | 34 | export const issue = z.object({ |
35 | description: z.string().nonempty(), | ||
36 | severity, | ||
37 | line: z.number().int(), | ||
38 | column: z.number().int().nonnegative(), | ||
39 | offset: z.number().int().nonnegative(), | ||
40 | length: z.number().int().nonnegative(), | ||
41 | }); | ||
156 | 42 | ||
157 | description?: string; | 43 | export type Issue = z.infer<typeof issue>; |
158 | 44 | ||
159 | documentation?: string; | 45 | export const validationResult = z.object({ |
46 | issues: issue.array(), | ||
47 | }); | ||
160 | 48 | ||
161 | escapePosition?: number; | 49 | export type ValidationResult = z.infer<typeof validationResult>; |
162 | 50 | ||
163 | textReplacements: IReplaceRegion[]; | 51 | export const replaceRegion = z.object({ |
52 | offset: z.number().int().nonnegative(), | ||
53 | length: z.number().int().nonnegative(), | ||
54 | text: z.string(), | ||
55 | }); | ||
164 | 56 | ||
165 | editPositions: ITextRegion[]; | 57 | export type ReplaceRegion = z.infer<typeof replaceRegion>; |
166 | 58 | ||
167 | kind: XtextContentAssistEntryKind | string; | 59 | export const textRegion = z.object({ |
168 | } | 60 | offset: z.number().int().nonnegative(), |
61 | length: z.number().int().nonnegative(), | ||
62 | }); | ||
169 | 63 | ||
170 | function isStringOrUndefined(value: unknown): value is string | undefined { | 64 | export type TextRegion = z.infer<typeof textRegion>; |
171 | return typeof value === 'string' || typeof value === 'undefined'; | ||
172 | } | ||
173 | 65 | ||
174 | function isNumberOrUndefined(value: unknown): value is number | undefined { | 66 | export const contentAssistEntry = z.object({ |
175 | return typeof value === 'number' || typeof value === 'undefined'; | 67 | prefix: z.string(), |
176 | } | 68 | proposal: z.string().nonempty(), |
69 | label: z.string().optional(), | ||
70 | description: z.string().nonempty().optional(), | ||
71 | documentation: z.string().nonempty().optional(), | ||
72 | escapePosition: z.number().int().nonnegative().optional(), | ||
73 | textReplacements: replaceRegion.array(), | ||
74 | editPositions: textRegion.array(), | ||
75 | kind: z.string().nonempty(), | ||
76 | }); | ||
177 | 77 | ||
178 | export function isContentAssistEntry(value: unknown): value is IContentAssistEntry { | 78 | export type ContentAssistEntry = z.infer<typeof contentAssistEntry>; |
179 | const entry = value as IContentAssistEntry; | ||
180 | return typeof entry === 'object' | ||
181 | && typeof entry.prefix === 'string' | ||
182 | && typeof entry.proposal === 'string' | ||
183 | && isStringOrUndefined(entry.label) | ||
184 | && isStringOrUndefined(entry.description) | ||
185 | && isStringOrUndefined(entry.documentation) | ||
186 | && isNumberOrUndefined(entry.escapePosition) | ||
187 | && isArrayOfType(entry.textReplacements, isReplaceRegion) | ||
188 | && isArrayOfType(entry.editPositions, isTextRegion) | ||
189 | && typeof entry.kind === 'string'; | ||
190 | } | ||
191 | 79 | ||
192 | export interface IContentAssistResult extends IDocumentStateResult { | 80 | export const contentAssistResult = documentStateResult.extend({ |
193 | entries: IContentAssistEntry[]; | 81 | entries: contentAssistEntry.array(), |
194 | } | 82 | }); |
195 | 83 | ||
196 | export function isContentAssistResult(result: unknown): result is IContentAssistResult { | 84 | export type ContentAssistResult = z.infer<typeof contentAssistResult>; |
197 | const contentAssistResult = result as IContentAssistResult; | ||
198 | return isDocumentStateResult(result) | ||
199 | && isArrayOfType(contentAssistResult.entries, isContentAssistEntry); | ||
200 | } | ||
201 | 85 | ||
202 | export interface IHighlightingRegion { | 86 | export const highlightingRegion = z.object({ |
203 | offset: number; | 87 | offset: z.number().int().nonnegative(), |
88 | length: z.number().int().nonnegative(), | ||
89 | styleClasses: z.string().nonempty().array(), | ||
90 | }); | ||
204 | 91 | ||
205 | length: number; | 92 | export type HighlightingRegion = z.infer<typeof highlightingRegion>; |
206 | 93 | ||
207 | styleClasses: string[]; | 94 | export const highlightingResult = z.object({ |
208 | } | 95 | regions: highlightingRegion.array(), |
96 | }); | ||
209 | 97 | ||
210 | export function isHighlightingRegion(value: unknown): value is IHighlightingRegion { | 98 | export type HighlightingResult = z.infer<typeof highlightingResult>; |
211 | const region = value as IHighlightingRegion; | ||
212 | return typeof region === 'object' | ||
213 | && typeof region.offset === 'number' | ||
214 | && typeof region.length === 'number' | ||
215 | && isArrayOfType(region.styleClasses, (s): s is string => typeof s === 'string'); | ||
216 | } | ||
217 | |||
218 | export interface IHighlightingResult { | ||
219 | regions: IHighlightingRegion[]; | ||
220 | } | ||
221 | 99 | ||
222 | export function isHighlightingResult(result: unknown): result is IHighlightingResult { | 100 | export const occurrencesResult = documentStateResult.extend({ |
223 | const highlightingResult = result as IHighlightingResult; | 101 | writeRegions: textRegion.array(), |
224 | return typeof highlightingResult === 'object' | 102 | readRegions: textRegion.array(), |
225 | && isArrayOfType(highlightingResult.regions, isHighlightingRegion); | 103 | }); |
226 | } | ||
227 | 104 | ||
228 | export interface IOccurrencesResult extends IDocumentStateResult { | 105 | export type OccurrencesResult = z.infer<typeof occurrencesResult>; |
229 | writeRegions: ITextRegion[]; | ||
230 | 106 | ||
231 | readRegions: ITextRegion[]; | 107 | export const formattingResult = documentStateResult.extend({ |
232 | } | 108 | formattedText: z.string(), |
109 | replaceRegion: textRegion, | ||
110 | }); | ||
233 | 111 | ||
234 | export function isOccurrencesResult(result: unknown): result is IOccurrencesResult { | 112 | export type FormattingResult = z.infer<typeof formattingResult>; |
235 | const occurrencesResult = result as IOccurrencesResult; | ||
236 | return isDocumentStateResult(occurrencesResult) | ||
237 | && isArrayOfType(occurrencesResult.writeRegions, isTextRegion) | ||
238 | && isArrayOfType(occurrencesResult.readRegions, isTextRegion); | ||
239 | } | ||