diff options
author | 2023-08-20 19:41:32 +0200 | |
---|---|---|
committer | 2023-08-20 20:29:02 +0200 | |
commit | a3f1e6872f4f768d14899a1e70bbdc14f32e478d (patch) | |
tree | b2daf0c81724f31ee190f5d63eb42988086dabf2 /subprojects/frontend/src/xtext | |
parent | fix: nullary model initialization (diff) | |
download | refinery-a3f1e6872f4f768d14899a1e70bbdc14f32e478d.tar.gz refinery-a3f1e6872f4f768d14899a1e70bbdc14f32e478d.tar.zst refinery-a3f1e6872f4f768d14899a1e70bbdc14f32e478d.zip |
feat: improve semantics error reporting
Also makes model seeds cancellable to reduce server load during semantic
analysis.
Diffstat (limited to 'subprojects/frontend/src/xtext')
5 files changed, 79 insertions, 9 deletions
diff --git a/subprojects/frontend/src/xtext/SemanticsService.ts b/subprojects/frontend/src/xtext/SemanticsService.ts new file mode 100644 index 00000000..50ec371a --- /dev/null +++ b/subprojects/frontend/src/xtext/SemanticsService.ts | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | import type EditorStore from '../editor/EditorStore'; | ||
8 | |||
9 | import type ValidationService from './ValidationService'; | ||
10 | import { SemanticsResult } from './xtextServiceResults'; | ||
11 | |||
12 | export default class SemanticsService { | ||
13 | constructor( | ||
14 | private readonly store: EditorStore, | ||
15 | private readonly validationService: ValidationService, | ||
16 | ) {} | ||
17 | |||
18 | onPush(push: unknown): void { | ||
19 | const result = SemanticsResult.parse(push); | ||
20 | this.validationService.setSemanticsIssues(result.issues ?? []); | ||
21 | if (result.error !== undefined) { | ||
22 | this.store.setSemanticsError(result.error); | ||
23 | } else { | ||
24 | this.store.setSemantics(push); | ||
25 | } | ||
26 | this.store.analysisCompleted(); | ||
27 | } | ||
28 | } | ||
diff --git a/subprojects/frontend/src/xtext/UpdateService.ts b/subprojects/frontend/src/xtext/UpdateService.ts index ee5ebde2..1ac722e1 100644 --- a/subprojects/frontend/src/xtext/UpdateService.ts +++ b/subprojects/frontend/src/xtext/UpdateService.ts | |||
@@ -133,6 +133,7 @@ export default class UpdateService { | |||
133 | return; | 133 | return; |
134 | } | 134 | } |
135 | log.trace('Editor delta', delta); | 135 | log.trace('Editor delta', delta); |
136 | this.store.analysisStarted(); | ||
136 | const result = await this.webSocketClient.send({ | 137 | const result = await this.webSocketClient.send({ |
137 | resource: this.resourceName, | 138 | resource: this.resourceName, |
138 | serviceType: 'update', | 139 | serviceType: 'update', |
@@ -157,6 +158,7 @@ export default class UpdateService { | |||
157 | private async updateFullTextExclusive(): Promise<void> { | 158 | private async updateFullTextExclusive(): Promise<void> { |
158 | log.debug('Performing full text update'); | 159 | log.debug('Performing full text update'); |
159 | this.tracker.prepareFullTextUpdateExclusive(); | 160 | this.tracker.prepareFullTextUpdateExclusive(); |
161 | this.store.analysisStarted(); | ||
160 | const result = await this.webSocketClient.send({ | 162 | const result = await this.webSocketClient.send({ |
161 | resource: this.resourceName, | 163 | resource: this.resourceName, |
162 | serviceType: 'update', | 164 | serviceType: 'update', |
diff --git a/subprojects/frontend/src/xtext/ValidationService.ts b/subprojects/frontend/src/xtext/ValidationService.ts index 64fb63eb..1a896db3 100644 --- a/subprojects/frontend/src/xtext/ValidationService.ts +++ b/subprojects/frontend/src/xtext/ValidationService.ts | |||
@@ -9,7 +9,7 @@ import type { Diagnostic } from '@codemirror/lint'; | |||
9 | import type EditorStore from '../editor/EditorStore'; | 9 | import type EditorStore from '../editor/EditorStore'; |
10 | 10 | ||
11 | import type UpdateService from './UpdateService'; | 11 | import type UpdateService from './UpdateService'; |
12 | import { ValidationResult } from './xtextServiceResults'; | 12 | import { Issue, ValidationResult } from './xtextServiceResults'; |
13 | 13 | ||
14 | export default class ValidationService { | 14 | export default class ValidationService { |
15 | constructor( | 15 | constructor( |
@@ -17,11 +17,41 @@ export default class ValidationService { | |||
17 | private readonly updateService: UpdateService, | 17 | private readonly updateService: UpdateService, |
18 | ) {} | 18 | ) {} |
19 | 19 | ||
20 | private lastValidationIssues: Issue[] = []; | ||
21 | |||
22 | private lastSemanticsIssues: Issue[] = []; | ||
23 | |||
20 | onPush(push: unknown): void { | 24 | onPush(push: unknown): void { |
21 | const { issues } = ValidationResult.parse(push); | 25 | ({ issues: this.lastValidationIssues } = ValidationResult.parse(push)); |
26 | this.lastSemanticsIssues = []; | ||
27 | this.updateDiagnostics(); | ||
28 | if ( | ||
29 | this.lastValidationIssues.some(({ severity }) => severity === 'error') | ||
30 | ) { | ||
31 | this.store.analysisCompleted(true); | ||
32 | } | ||
33 | } | ||
34 | |||
35 | onDisconnect(): void { | ||
36 | this.store.updateDiagnostics([]); | ||
37 | this.lastValidationIssues = []; | ||
38 | this.lastSemanticsIssues = []; | ||
39 | } | ||
40 | |||
41 | setSemanticsIssues(issues: Issue[]): void { | ||
42 | this.lastSemanticsIssues = issues; | ||
43 | this.updateDiagnostics(); | ||
44 | } | ||
45 | |||
46 | private updateDiagnostics(): void { | ||
22 | const allChanges = this.updateService.computeChangesSinceLastUpdate(); | 47 | const allChanges = this.updateService.computeChangesSinceLastUpdate(); |
23 | const diagnostics: Diagnostic[] = []; | 48 | const diagnostics: Diagnostic[] = []; |
24 | issues.forEach(({ offset, length, severity, description }) => { | 49 | function createDiagnostic({ |
50 | offset, | ||
51 | length, | ||
52 | severity, | ||
53 | description, | ||
54 | }: Issue): void { | ||
25 | if (severity === 'ignore') { | 55 | if (severity === 'ignore') { |
26 | return; | 56 | return; |
27 | } | 57 | } |
@@ -31,11 +61,9 @@ export default class ValidationService { | |||
31 | severity, | 61 | severity, |
32 | message: description, | 62 | message: description, |
33 | }); | 63 | }); |
34 | }); | 64 | } |
65 | this.lastValidationIssues.forEach(createDiagnostic); | ||
66 | this.lastSemanticsIssues.forEach(createDiagnostic); | ||
35 | this.store.updateDiagnostics(diagnostics); | 67 | this.store.updateDiagnostics(diagnostics); |
36 | } | 68 | } |
37 | |||
38 | onDisconnect(): void { | ||
39 | this.store.updateDiagnostics([]); | ||
40 | } | ||
41 | } | 69 | } |
diff --git a/subprojects/frontend/src/xtext/XtextClient.ts b/subprojects/frontend/src/xtext/XtextClient.ts index d145cd30..87778084 100644 --- a/subprojects/frontend/src/xtext/XtextClient.ts +++ b/subprojects/frontend/src/xtext/XtextClient.ts | |||
@@ -17,6 +17,7 @@ import getLogger from '../utils/getLogger'; | |||
17 | import ContentAssistService from './ContentAssistService'; | 17 | import ContentAssistService from './ContentAssistService'; |
18 | import HighlightingService from './HighlightingService'; | 18 | import HighlightingService from './HighlightingService'; |
19 | import OccurrencesService from './OccurrencesService'; | 19 | import OccurrencesService from './OccurrencesService'; |
20 | import SemanticsService from './SemanticsService'; | ||
20 | import UpdateService from './UpdateService'; | 21 | import UpdateService from './UpdateService'; |
21 | import ValidationService from './ValidationService'; | 22 | import ValidationService from './ValidationService'; |
22 | import XtextWebSocketClient from './XtextWebSocketClient'; | 23 | import XtextWebSocketClient from './XtextWebSocketClient'; |
@@ -37,6 +38,8 @@ export default class XtextClient { | |||
37 | 38 | ||
38 | private readonly occurrencesService: OccurrencesService; | 39 | private readonly occurrencesService: OccurrencesService; |
39 | 40 | ||
41 | private readonly semanticsService: SemanticsService; | ||
42 | |||
40 | constructor( | 43 | constructor( |
41 | private readonly store: EditorStore, | 44 | private readonly store: EditorStore, |
42 | private readonly pwaStore: PWAStore, | 45 | private readonly pwaStore: PWAStore, |
@@ -54,6 +57,7 @@ export default class XtextClient { | |||
54 | ); | 57 | ); |
55 | this.validationService = new ValidationService(store, this.updateService); | 58 | this.validationService = new ValidationService(store, this.updateService); |
56 | this.occurrencesService = new OccurrencesService(store, this.updateService); | 59 | this.occurrencesService = new OccurrencesService(store, this.updateService); |
60 | this.semanticsService = new SemanticsService(store, this.validationService); | ||
57 | } | 61 | } |
58 | 62 | ||
59 | start(): void { | 63 | start(): void { |
@@ -67,6 +71,7 @@ export default class XtextClient { | |||
67 | } | 71 | } |
68 | 72 | ||
69 | private onDisconnect(): void { | 73 | private onDisconnect(): void { |
74 | this.store.analysisCompleted(true); | ||
70 | this.highlightingService.onDisconnect(); | 75 | this.highlightingService.onDisconnect(); |
71 | this.validationService.onDisconnect(); | 76 | this.validationService.onDisconnect(); |
72 | this.occurrencesService.onDisconnect(); | 77 | this.occurrencesService.onDisconnect(); |
@@ -115,7 +120,7 @@ export default class XtextClient { | |||
115 | this.validationService.onPush(push); | 120 | this.validationService.onPush(push); |
116 | return; | 121 | return; |
117 | case 'semantics': | 122 | case 'semantics': |
118 | this.store.setSemantics(push); | 123 | this.semanticsService.onPush(push); |
119 | return; | 124 | return; |
120 | default: | 125 | default: |
121 | throw new Error('Unknown service'); | 126 | throw new Error('Unknown service'); |
diff --git a/subprojects/frontend/src/xtext/xtextServiceResults.ts b/subprojects/frontend/src/xtext/xtextServiceResults.ts index d3b467ad..cae95771 100644 --- a/subprojects/frontend/src/xtext/xtextServiceResults.ts +++ b/subprojects/frontend/src/xtext/xtextServiceResults.ts | |||
@@ -125,3 +125,10 @@ export const FormattingResult = DocumentStateResult.extend({ | |||
125 | }); | 125 | }); |
126 | 126 | ||
127 | export type FormattingResult = z.infer<typeof FormattingResult>; | 127 | export type FormattingResult = z.infer<typeof FormattingResult>; |
128 | |||
129 | export const SemanticsResult = z.object({ | ||
130 | error: z.string().optional(), | ||
131 | issues: Issue.array().optional(), | ||
132 | }); | ||
133 | |||
134 | export type SemanticsResult = z.infer<typeof SemanticsResult>; | ||