diff options
Diffstat (limited to 'language-web/src/main/js/xtext/xtextServiceResults.ts')
-rw-r--r-- | language-web/src/main/js/xtext/xtextServiceResults.ts | 284 |
1 files changed, 75 insertions, 209 deletions
diff --git a/language-web/src/main/js/xtext/xtextServiceResults.ts b/language-web/src/main/js/xtext/xtextServiceResults.ts index b2de1e4a..b6867e2f 100644 --- a/language-web/src/main/js/xtext/xtextServiceResults.ts +++ b/language-web/src/main/js/xtext/xtextServiceResults.ts | |||
@@ -1,239 +1,105 @@ | |||
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 | |||
62 | length: number; | ||
63 | } | ||
64 | 2 | ||
65 | export function isIssue(value: unknown): value is IIssue { | 3 | export const pongResult = z.object({ |
66 | const issue = value as IIssue; | 4 | pong: z.string().nonempty(), |
67 | return typeof issue === 'object' | 5 | }); |
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 | 6 | ||
76 | export interface IValidationResult { | 7 | export type PongResult = z.infer<typeof pongResult>; |
77 | issues: IIssue[]; | ||
78 | } | ||
79 | 8 | ||
80 | function isArrayOfType<T>(value: unknown, check: (entry: unknown) => entry is T): value is T[] { | 9 | export const documentStateResult = z.object({ |
81 | return Array.isArray(value) && (value as T[]).every(check); | 10 | stateId: z.string().nonempty(), |
82 | } | 11 | }); |
83 | 12 | ||
84 | export function isValidationResult(result: unknown): result is IValidationResult { | 13 | export type DocumentStateResult = z.infer<typeof documentStateResult>; |
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 | 14 | ||
93 | length: number; | 15 | export const conflict = z.enum(['invalidStateId', 'canceled']); |
94 | 16 | ||
95 | text: string; | 17 | export type Conflict = z.infer<typeof conflict>; |
96 | } | ||
97 | 18 | ||
98 | export function isReplaceRegion(value: unknown): value is IReplaceRegion { | 19 | export const serviceConflictResult = z.object({ |
99 | const replaceRegion = value as IReplaceRegion; | 20 | conflict, |
100 | return typeof replaceRegion === 'object' | 21 | }); |
101 | && typeof replaceRegion.offset === 'number' | ||
102 | && typeof replaceRegion.length === 'number' | ||
103 | && typeof replaceRegion.text === 'string'; | ||
104 | } | ||
105 | 22 | ||
106 | export interface ITextRegion { | 23 | export type ServiceConflictResult = z.infer<typeof serviceConflictResult>; |
107 | offset: number; | ||
108 | 24 | ||
109 | length: number; | 25 | export function isConflictResult(result: unknown, conflictType: Conflict): boolean { |
26 | const parsedConflictResult = serviceConflictResult.safeParse(result); | ||
27 | return parsedConflictResult.success && parsedConflictResult.data.conflict === conflictType; | ||
110 | } | 28 | } |
111 | 29 | ||
112 | export function isTextRegion(value: unknown): value is ITextRegion { | 30 | export const severity = z.enum(['error', 'warning', 'info', 'ignore']); |
113 | const textRegion = value as ITextRegion; | ||
114 | return typeof textRegion === 'object' | ||
115 | && typeof textRegion.offset === 'number' | ||
116 | && typeof textRegion.length === 'number'; | ||
117 | } | ||
118 | 31 | ||
119 | export const VALID_XTEXT_CONTENT_ASSIST_ENTRY_KINDS = [ | 32 | export type Severity = z.infer<typeof severity>; |
120 | 'TEXT', | ||
121 | 'METHOD', | ||
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 | } | ||
149 | 33 | ||
150 | export interface IContentAssistEntry { | 34 | export const issue = z.object({ |
151 | prefix: string; | 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 | }); | ||
152 | 42 | ||
153 | proposal: string; | 43 | export type Issue = z.infer<typeof issue>; |
154 | 44 | ||
155 | label?: string; | 45 | export const validationResult = z.object({ |
46 | issues: issue.array(), | ||
47 | }); | ||
156 | 48 | ||
157 | description?: string; | 49 | export type ValidationResult = z.infer<typeof validationResult>; |
158 | 50 | ||
159 | documentation?: string; | 51 | export const replaceRegion = z.object({ |
52 | offset: z.number().int().nonnegative(), | ||
53 | length: z.number().int().nonnegative(), | ||
54 | text: z.string(), | ||
55 | }); | ||
160 | 56 | ||
161 | escapePosition?: number; | 57 | export type ReplaceRegion = z.infer<typeof replaceRegion>; |
162 | 58 | ||
163 | textReplacements: IReplaceRegion[]; | 59 | export const textRegion = z.object({ |
60 | offset: z.number().int().nonnegative(), | ||
61 | length: z.number().int().nonnegative(), | ||
62 | }); | ||
164 | 63 | ||
165 | editPositions: ITextRegion[]; | 64 | export type TextRegion = z.infer<typeof textRegion>; |
166 | 65 | ||
167 | kind: XtextContentAssistEntryKind | string; | 66 | export const contentAssistEntry = z.object({ |
168 | } | 67 | prefix: z.string(), |
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 | }); | ||
169 | 77 | ||
170 | function isStringOrUndefined(value: unknown): value is string | undefined { | 78 | export type ContentAssistEntry = z.infer<typeof contentAssistEntry>; |
171 | return typeof value === 'string' || typeof value === 'undefined'; | ||
172 | } | ||
173 | 79 | ||
174 | function isNumberOrUndefined(value: unknown): value is number | undefined { | 80 | export const contentAssistResult = documentStateResult.extend({ |
175 | return typeof value === 'number' || typeof value === 'undefined'; | 81 | entries: contentAssistEntry.array(), |
176 | } | 82 | }); |
177 | 83 | ||
178 | export function isContentAssistEntry(value: unknown): value is IContentAssistEntry { | 84 | export type ContentAssistResult = z.infer<typeof contentAssistResult>; |
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 | 85 | ||
192 | export interface IContentAssistResult extends IDocumentStateResult { | 86 | export const highlightingRegion = z.object({ |
193 | entries: IContentAssistEntry[]; | 87 | offset: z.number().int().nonnegative(), |
194 | } | 88 | length: z.number().int().nonnegative(), |
195 | 89 | styleClasses: z.string().nonempty().array(), | |
196 | export function isContentAssistResult(result: unknown): result is IContentAssistResult { | 90 | }); |
197 | const contentAssistResult = result as IContentAssistResult; | ||
198 | return isDocumentStateResult(result) | ||
199 | && isArrayOfType(contentAssistResult.entries, isContentAssistEntry); | ||
200 | } | ||
201 | 91 | ||
202 | export interface IHighlightingRegion { | 92 | export type HighlightingRegion = z.infer<typeof highlightingRegion>; |
203 | offset: number; | ||
204 | 93 | ||
205 | length: number; | 94 | export const highlightingResult = z.object({ |
95 | regions: highlightingRegion.array(), | ||
96 | }); | ||
206 | 97 | ||
207 | styleClasses: string[]; | 98 | export type HighlightingResult = z.infer<typeof highlightingResult>; |
208 | } | ||
209 | 99 | ||
210 | export function isHighlightingRegion(value: unknown): value is IHighlightingRegion { | 100 | export const occurrencesResult = documentStateResult.extend({ |
211 | const region = value as IHighlightingRegion; | 101 | writeRegions: textRegion.array(), |
212 | return typeof region === 'object' | 102 | readRegions: textRegion.array(), |
213 | && typeof region.offset === 'number' | 103 | }); |
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 | 104 | ||
222 | export function isHighlightingResult(result: unknown): result is IHighlightingResult { | 105 | export type OccurrencesResult = z.infer<typeof occurrencesResult>; |
223 | const highlightingResult = result as IHighlightingResult; | ||
224 | return typeof highlightingResult === 'object' | ||
225 | && isArrayOfType(highlightingResult.regions, isHighlightingRegion); | ||
226 | } | ||
227 | |||
228 | export interface IOccurrencesResult extends IDocumentStateResult { | ||
229 | writeRegions: ITextRegion[]; | ||
230 | |||
231 | readRegions: ITextRegion[]; | ||
232 | } | ||
233 | |||
234 | export function isOccurrencesResult(result: unknown): result is IOccurrencesResult { | ||
235 | const occurrencesResult = result as IOccurrencesResult; | ||
236 | return isDocumentStateResult(occurrencesResult) | ||
237 | && isArrayOfType(occurrencesResult.writeRegions, isTextRegion) | ||
238 | && isArrayOfType(occurrencesResult.readRegions, isTextRegion); | ||
239 | } | ||