diff options
author | Kristóf Marussy <marussy@mit.bme.hu> | 2021-06-29 11:37:14 +0200 |
---|---|---|
committer | Kristóf Marussy <marussy@mit.bme.hu> | 2021-06-29 11:37:14 +0200 |
commit | 272c7c5dd04feb54806b92d88dc1a5029cddc397 (patch) | |
tree | decf6950c10782d124c5a3b6055a09d52f9e5791 /language-web/src/main/js/xtext/xtext-codemirror.js | |
parent | Fix derived state computer idempotency (diff) | |
download | refinery-272c7c5dd04feb54806b92d88dc1a5029cddc397.tar.gz refinery-272c7c5dd04feb54806b92d88dc1a5029cddc397.tar.zst refinery-272c7c5dd04feb54806b92d88dc1a5029cddc397.zip |
Webpack build for frontend
Diffstat (limited to 'language-web/src/main/js/xtext/xtext-codemirror.js')
-rw-r--r-- | language-web/src/main/js/xtext/xtext-codemirror.js | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/language-web/src/main/js/xtext/xtext-codemirror.js b/language-web/src/main/js/xtext/xtext-codemirror.js new file mode 100644 index 00000000..4d50718c --- /dev/null +++ b/language-web/src/main/js/xtext/xtext-codemirror.js | |||
@@ -0,0 +1,472 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2015, 2017 itemis AG (http://www.itemis.eu) and others. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-2.0. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | |||
10 | /* | ||
11 | * Use `createEditor(options)` to create an Xtext editor. You can specify options either | ||
12 | * through the function parameter or through `data-editor-x` attributes, where x is an | ||
13 | * option name with camelCase converted to hyphen-separated. | ||
14 | * In addition to the options supported by CodeMirror (https://codemirror.net/doc/manual.html#config), | ||
15 | * the following options are available: | ||
16 | * | ||
17 | * baseUrl = "/" {String} | ||
18 | * The path segment where the Xtext service is found; see serviceUrl option. | ||
19 | * contentType {String} | ||
20 | * The content type included in requests to the Xtext server. | ||
21 | * dirtyElement {String | DOMElement} | ||
22 | * An element into which the dirty status class is written when the editor is marked dirty; | ||
23 | * it can be either a DOM element or an ID for a DOM element. | ||
24 | * dirtyStatusClass = 'dirty' {String} | ||
25 | * A CSS class name written into the dirtyElement when the editor is marked dirty. | ||
26 | * document {Document} | ||
27 | * The document; if not specified, the global document is used. | ||
28 | * enableContentAssistService = true {Boolean} | ||
29 | * Whether content assist should be enabled. | ||
30 | * enableCors = true {Boolean} | ||
31 | * Whether CORS should be enabled for service request. | ||
32 | * enableFormattingAction = false {Boolean} | ||
33 | * Whether the formatting action should be bound to the standard keystroke ctrl+shift+s / cmd+shift+f. | ||
34 | * enableFormattingService = true {Boolean} | ||
35 | * Whether text formatting should be enabled. | ||
36 | * enableGeneratorService = true {Boolean} | ||
37 | * Whether code generation should be enabled (must be triggered through JavaScript code). | ||
38 | * enableHighlightingService = true {Boolean} | ||
39 | * Whether semantic highlighting (computed on the server) should be enabled. | ||
40 | * enableOccurrencesService = true {Boolean} | ||
41 | * Whether marking occurrences should be enabled. | ||
42 | * enableSaveAction = false {Boolean} | ||
43 | * Whether the save action should be bound to the standard keystroke ctrl+s / cmd+s. | ||
44 | * enableValidationService = true {Boolean} | ||
45 | * Whether validation should be enabled. | ||
46 | * loadFromServer = true {Boolean} | ||
47 | * Whether to load the editor content from the server. | ||
48 | * mode {String} | ||
49 | * The name of the syntax highlighting mode to use; the mode has to be registered externally | ||
50 | * (see CodeMirror documentation). | ||
51 | * parent = 'xtext-editor' {String | DOMElement} | ||
52 | * The parent element for the view; it can be either a DOM element or an ID for a DOM element. | ||
53 | * parentClass = 'xtext-editor' {String} | ||
54 | * If the 'parent' option is not given, this option is used to find elements that match the given class name. | ||
55 | * resourceId {String} | ||
56 | * The identifier of the resource displayed in the text editor; this option is sent to the server to | ||
57 | * communicate required information on the respective resource. | ||
58 | * selectionUpdateDelay = 550 {Number} | ||
59 | * The number of milliseconds to wait after a selection change before Xtext services are invoked. | ||
60 | * sendFullText = false {Boolean} | ||
61 | * Whether the full text shall be sent to the server with each request; use this if you want | ||
62 | * the server to run in stateless mode. If the option is inactive, the server state is updated regularly. | ||
63 | * serviceUrl {String} | ||
64 | * The URL of the Xtext servlet; if no value is given, it is constructed using the baseUrl option in the form | ||
65 | * {location.protocol}//{location.host}{baseUrl}xtext-service | ||
66 | * showErrorDialogs = false {Boolean} | ||
67 | * Whether errors should be displayed in popup dialogs. | ||
68 | * syntaxDefinition {String} | ||
69 | * If the 'mode' option is not set, the default mode 'xtext/{xtextLang}' is used. Set this option to | ||
70 | * 'none' to suppress this behavior and disable syntax highlighting. | ||
71 | * textUpdateDelay = 500 {Number} | ||
72 | * The number of milliseconds to wait after a text change before Xtext services are invoked. | ||
73 | * xtextLang {String} | ||
74 | * The language name (usually the file extension configured for the language). | ||
75 | */ | ||
76 | define([ | ||
77 | 'jquery', | ||
78 | 'codemirror', | ||
79 | 'codemirror/addon/hint/show-hint', | ||
80 | 'xtext/compatibility', | ||
81 | 'xtext/ServiceBuilder', | ||
82 | 'xtext/CodeMirrorEditorContext', | ||
83 | 'codemirror/mode/javascript/javascript' | ||
84 | ], function(jQuery, CodeMirror, ShowHint, compatibility, ServiceBuilder, EditorContext) { | ||
85 | |||
86 | var exports = {}; | ||
87 | |||
88 | /** | ||
89 | * Create one or more Xtext editor instances configured with the given options. | ||
90 | * The return value is either a CodeMirror editor or an array of CodeMirror editors. | ||
91 | */ | ||
92 | exports.createEditor = function(options) { | ||
93 | if (!options) | ||
94 | options = {}; | ||
95 | |||
96 | var query; | ||
97 | if (jQuery.type(options.parent) === 'string') { | ||
98 | query = jQuery('#' + options.parent, options.document); | ||
99 | } else if (options.parent) { | ||
100 | query = jQuery(options.parent); | ||
101 | } else if (jQuery.type(options.parentClass) === 'string') { | ||
102 | query = jQuery('.' + options.parentClass, options.document); | ||
103 | } else { | ||
104 | query = jQuery('#xtext-editor', options.document); | ||
105 | if (query.length == 0) | ||
106 | query = jQuery('.xtext-editor', options.document); | ||
107 | } | ||
108 | |||
109 | var editors = []; | ||
110 | query.each(function(index, parent) { | ||
111 | var editorOptions = ServiceBuilder.mergeParentOptions(parent, options); | ||
112 | if (!editorOptions.value) | ||
113 | editorOptions.value = jQuery(parent).text(); | ||
114 | var editor = CodeMirror(function(element) { | ||
115 | jQuery(parent).empty().append(element); | ||
116 | }, editorOptions); | ||
117 | |||
118 | exports.createServices(editor, editorOptions); | ||
119 | editors[index] = editor; | ||
120 | }); | ||
121 | |||
122 | if (editors.length == 1) | ||
123 | return editors[0]; | ||
124 | else | ||
125 | return editors; | ||
126 | } | ||
127 | |||
128 | function CodeMirrorServiceBuilder(editor, xtextServices) { | ||
129 | this.editor = editor; | ||
130 | xtextServices.editorContext._highlightingMarkers = []; | ||
131 | xtextServices.editorContext._validationMarkers = []; | ||
132 | xtextServices.editorContext._occurrenceMarkers = []; | ||
133 | ServiceBuilder.call(this, xtextServices); | ||
134 | } | ||
135 | CodeMirrorServiceBuilder.prototype = new ServiceBuilder(); | ||
136 | |||
137 | /** | ||
138 | * Configure Xtext services for the given editor. The editor does not have to be created | ||
139 | * with createEditor(options). | ||
140 | */ | ||
141 | exports.createServices = function(editor, options) { | ||
142 | if (options.enableValidationService || options.enableValidationService === undefined) { | ||
143 | editor.setOption('gutters', ['annotations-gutter']); | ||
144 | } | ||
145 | var xtextServices = { | ||
146 | options: options, | ||
147 | editorContext: new EditorContext(editor) | ||
148 | }; | ||
149 | var serviceBuilder = new CodeMirrorServiceBuilder(editor, xtextServices); | ||
150 | serviceBuilder.createServices(); | ||
151 | xtextServices.serviceBuilder = serviceBuilder; | ||
152 | editor.xtextServices = xtextServices; | ||
153 | return xtextServices; | ||
154 | } | ||
155 | |||
156 | /** | ||
157 | * Remove all services and listeners that have been previously created with createServices(editor, options). | ||
158 | */ | ||
159 | exports.removeServices = function(editor) { | ||
160 | if (!editor.xtextServices) | ||
161 | return; | ||
162 | var services = editor.xtextServices; | ||
163 | if (services.modelChangeListener) | ||
164 | editor.off('changes', services.modelChangeListener); | ||
165 | if (services.cursorActivityListener) | ||
166 | editor.off('cursorActivity', services.cursorActivityListener); | ||
167 | if (services.saveKeyMap) | ||
168 | editor.removeKeyMap(services.saveKeyMap); | ||
169 | if (services.contentAssistKeyMap) | ||
170 | editor.removeKeyMap(services.contentAssistKeyMap); | ||
171 | if (services.formatKeyMap) | ||
172 | editor.removeKeyMap(services.formatKeyMap); | ||
173 | var editorContext = services.editorContext; | ||
174 | var highlightingMarkers = editorContext._highlightingMarkers; | ||
175 | if (highlightingMarkers) { | ||
176 | for (var i = 0; i < highlightingMarkers.length; i++) { | ||
177 | highlightingMarkers[i].clear(); | ||
178 | } | ||
179 | } | ||
180 | if (editorContext._validationAnnotations) | ||
181 | services.serviceBuilder._clearAnnotations(editorContext._validationAnnotations); | ||
182 | var validationMarkers = editorContext._validationMarkers; | ||
183 | if (validationMarkers) { | ||
184 | for (var i = 0; i < validationMarkers.length; i++) { | ||
185 | validationMarkers[i].clear(); | ||
186 | } | ||
187 | } | ||
188 | var occurrenceMarkers = editorContext._occurrenceMarkers; | ||
189 | if (occurrenceMarkers) { | ||
190 | for (var i = 0; i < occurrenceMarkers.length; i++) { | ||
191 | occurrenceMarkers[i].clear(); | ||
192 | } | ||
193 | } | ||
194 | delete editor.xtextServices; | ||
195 | } | ||
196 | |||
197 | /** | ||
198 | * Syntax highlighting (without semantic highlighting). | ||
199 | */ | ||
200 | CodeMirrorServiceBuilder.prototype.setupSyntaxHighlighting = function() { | ||
201 | var options = this.services.options; | ||
202 | // If the mode option is set, syntax highlighting has already been configured by CM | ||
203 | if (!options.mode && options.syntaxDefinition != 'none' && options.xtextLang) { | ||
204 | this.editor.setOption('mode', 'xtext/' + options.xtextLang); | ||
205 | } | ||
206 | } | ||
207 | |||
208 | /** | ||
209 | * Document update service. | ||
210 | */ | ||
211 | CodeMirrorServiceBuilder.prototype.setupUpdateService = function(refreshDocument) { | ||
212 | var services = this.services; | ||
213 | var editorContext = services.editorContext; | ||
214 | var textUpdateDelay = services.options.textUpdateDelay; | ||
215 | if (!textUpdateDelay) | ||
216 | textUpdateDelay = 500; | ||
217 | services.modelChangeListener = function(event) { | ||
218 | if (!event._xtext_init) | ||
219 | editorContext.setDirty(true); | ||
220 | if (editorContext._modelChangeTimeout) | ||
221 | clearTimeout(editorContext._modelChangeTimeout); | ||
222 | editorContext._modelChangeTimeout = setTimeout(function() { | ||
223 | if (services.options.sendFullText) | ||
224 | refreshDocument(); | ||
225 | else | ||
226 | services.update(); | ||
227 | }, textUpdateDelay); | ||
228 | } | ||
229 | if (!services.options.resourceId || !services.options.loadFromServer) | ||
230 | services.modelChangeListener({_xtext_init: true}); | ||
231 | this.editor.on('changes', services.modelChangeListener); | ||
232 | } | ||
233 | |||
234 | /** | ||
235 | * Persistence services: load, save, and revert. | ||
236 | */ | ||
237 | CodeMirrorServiceBuilder.prototype.setupPersistenceServices = function() { | ||
238 | var services = this.services; | ||
239 | if (services.options.enableSaveAction) { | ||
240 | var userAgent = navigator.userAgent.toLowerCase(); | ||
241 | var saveFunction = function(editor) { | ||
242 | services.saveResource(); | ||
243 | }; | ||
244 | services.saveKeyMap = /mac os/.test(userAgent) ? {'Cmd-S': saveFunction}: {'Ctrl-S': saveFunction}; | ||
245 | this.editor.addKeyMap(services.saveKeyMap); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | /** | ||
250 | * Content assist service. | ||
251 | */ | ||
252 | CodeMirrorServiceBuilder.prototype.setupContentAssistService = function() { | ||
253 | var services = this.services; | ||
254 | var editorContext = services.editorContext; | ||
255 | services.contentAssistKeyMap = {'Ctrl-Space': function(editor) { | ||
256 | var params = ServiceBuilder.copy(services.options); | ||
257 | var cursor = editor.getCursor(); | ||
258 | params.offset = editor.indexFromPos(cursor); | ||
259 | services.contentAssistService.invoke(editorContext, params).done(function(entries) { | ||
260 | editor.showHint({hint: function(editor, options) { | ||
261 | return { | ||
262 | list: entries.map(function(entry) { | ||
263 | var displayText; | ||
264 | if (entry.label) | ||
265 | displayText = entry.label; | ||
266 | else | ||
267 | displayText = entry.proposal; | ||
268 | if (entry.description) | ||
269 | displayText += ' (' + entry.description + ')'; | ||
270 | var prefixLength = 0 | ||
271 | if (entry.prefix) | ||
272 | prefixLength = entry.prefix.length | ||
273 | return { | ||
274 | text: entry.proposal, | ||
275 | displayText: displayText, | ||
276 | from: { | ||
277 | line: cursor.line, | ||
278 | ch: cursor.ch - prefixLength | ||
279 | } | ||
280 | }; | ||
281 | }), | ||
282 | to: cursor | ||
283 | }; | ||
284 | }}); | ||
285 | }); | ||
286 | }}; | ||
287 | this.editor.addKeyMap(services.contentAssistKeyMap); | ||
288 | } | ||
289 | |||
290 | /** | ||
291 | * Semantic highlighting service. | ||
292 | */ | ||
293 | CodeMirrorServiceBuilder.prototype.doHighlighting = function() { | ||
294 | var services = this.services; | ||
295 | var editorContext = services.editorContext; | ||
296 | var editor = this.editor; | ||
297 | services.computeHighlighting().always(function() { | ||
298 | var highlightingMarkers = editorContext._highlightingMarkers; | ||
299 | if (highlightingMarkers) { | ||
300 | for (var i = 0; i < highlightingMarkers.length; i++) { | ||
301 | highlightingMarkers[i].clear(); | ||
302 | } | ||
303 | } | ||
304 | editorContext._highlightingMarkers = []; | ||
305 | }).done(function(result) { | ||
306 | for (var i = 0; i < result.regions.length; ++i) { | ||
307 | var region = result.regions[i]; | ||
308 | var from = editor.posFromIndex(region.offset); | ||
309 | var to = editor.posFromIndex(region.offset + region.length); | ||
310 | region.styleClasses.forEach(function(styleClass) { | ||
311 | var marker = editor.markText(from, to, {className: styleClass}); | ||
312 | editorContext._highlightingMarkers.push(marker); | ||
313 | }); | ||
314 | } | ||
315 | }); | ||
316 | } | ||
317 | |||
318 | var annotationWeight = { | ||
319 | error: 30, | ||
320 | warning: 20, | ||
321 | info: 10 | ||
322 | }; | ||
323 | CodeMirrorServiceBuilder.prototype._getAnnotationWeight = function(annotation) { | ||
324 | if (annotationWeight[annotation] !== undefined) | ||
325 | return annotationWeight[annotation]; | ||
326 | else | ||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | CodeMirrorServiceBuilder.prototype._clearAnnotations = function(annotations) { | ||
331 | var editor = this.editor; | ||
332 | for (var i = 0; i < annotations.length; i++) { | ||
333 | var annotation = annotations[i]; | ||
334 | if (annotation) { | ||
335 | editor.setGutterMarker(i, 'annotations-gutter', null); | ||
336 | annotations[i] = undefined; | ||
337 | } | ||
338 | } | ||
339 | } | ||
340 | |||
341 | CodeMirrorServiceBuilder.prototype._refreshAnnotations = function(annotations) { | ||
342 | var editor = this.editor; | ||
343 | for (var i = 0; i < annotations.length; i++) { | ||
344 | var annotation = annotations[i]; | ||
345 | if (annotation) { | ||
346 | var classProp = ' class="xtext-annotation_' + annotation.type + '"'; | ||
347 | var titleProp = annotation.description ? ' title="' + annotation.description.replace(/"/g, '"') + '"' : ''; | ||
348 | var element = jQuery('<div' + classProp + titleProp + '></div>').get(0); | ||
349 | editor.setGutterMarker(i, 'annotations-gutter', element); | ||
350 | } | ||
351 | } | ||
352 | } | ||
353 | |||
354 | /** | ||
355 | * Validation service. | ||
356 | */ | ||
357 | CodeMirrorServiceBuilder.prototype.doValidation = function() { | ||
358 | var services = this.services; | ||
359 | var editorContext = services.editorContext; | ||
360 | var editor = this.editor; | ||
361 | var self = this; | ||
362 | services.validate().always(function() { | ||
363 | if (editorContext._validationAnnotations) | ||
364 | self._clearAnnotations(editorContext._validationAnnotations); | ||
365 | else | ||
366 | editorContext._validationAnnotations = []; | ||
367 | var validationMarkers = editorContext._validationMarkers; | ||
368 | if (validationMarkers) { | ||
369 | for (var i = 0; i < validationMarkers.length; i++) { | ||
370 | validationMarkers[i].clear(); | ||
371 | } | ||
372 | } | ||
373 | editorContext._validationMarkers = []; | ||
374 | }).done(function(result) { | ||
375 | var validationAnnotations = editorContext._validationAnnotations; | ||
376 | for (var i = 0; i < result.issues.length; i++) { | ||
377 | var entry = result.issues[i]; | ||
378 | var annotation = validationAnnotations[entry.line - 1]; | ||
379 | var weight = self._getAnnotationWeight(entry.severity); | ||
380 | if (annotation) { | ||
381 | if (annotation.weight < weight) { | ||
382 | annotation.type = entry.severity; | ||
383 | annotation.weight = weight; | ||
384 | } | ||
385 | if (annotation.description) | ||
386 | annotation.description += '\n' + entry.description; | ||
387 | else | ||
388 | annotation.description = entry.description; | ||
389 | } else { | ||
390 | validationAnnotations[entry.line - 1] = { | ||
391 | type: entry.severity, | ||
392 | weight: weight, | ||
393 | description: entry.description | ||
394 | }; | ||
395 | } | ||
396 | var from = editor.posFromIndex(entry.offset); | ||
397 | var to = editor.posFromIndex(entry.offset + entry.length); | ||
398 | var marker = editor.markText(from, to, { | ||
399 | className: 'xtext-marker_' + entry.severity, | ||
400 | title: entry.description | ||
401 | }); | ||
402 | editorContext._validationMarkers.push(marker); | ||
403 | } | ||
404 | self._refreshAnnotations(validationAnnotations); | ||
405 | }); | ||
406 | } | ||
407 | |||
408 | /** | ||
409 | * Occurrences service. | ||
410 | */ | ||
411 | CodeMirrorServiceBuilder.prototype.setupOccurrencesService = function() { | ||
412 | var services = this.services; | ||
413 | var editorContext = services.editorContext; | ||
414 | var selectionUpdateDelay = services.options.selectionUpdateDelay; | ||
415 | if (!selectionUpdateDelay) | ||
416 | selectionUpdateDelay = 550; | ||
417 | var editor = this.editor; | ||
418 | var self = this; | ||
419 | services.cursorActivityListener = function() { | ||
420 | if (editorContext._selectionChangeTimeout) { | ||
421 | clearTimeout(editorContext._selectionChangeTimeout); | ||
422 | } | ||
423 | editorContext._selectionChangeTimeout = setTimeout(function() { | ||
424 | var params = ServiceBuilder.copy(services.options); | ||
425 | var cursor = editor.getCursor(); | ||
426 | params.offset = editor.indexFromPos(cursor); | ||
427 | services.occurrencesService.invoke(editorContext, params).always(function() { | ||
428 | var occurrenceMarkers = editorContext._occurrenceMarkers; | ||
429 | if (occurrenceMarkers) { | ||
430 | for (var i = 0; i < occurrenceMarkers.length; i++) { | ||
431 | occurrenceMarkers[i].clear(); | ||
432 | } | ||
433 | } | ||
434 | editorContext._occurrenceMarkers = []; | ||
435 | }).done(function(occurrencesResult) { | ||
436 | for (var i = 0; i < occurrencesResult.readRegions.length; i++) { | ||
437 | var region = occurrencesResult.readRegions[i]; | ||
438 | var from = editor.posFromIndex(region.offset); | ||
439 | var to = editor.posFromIndex(region.offset + region.length); | ||
440 | var marker = editor.markText(from, to, {className: 'xtext-marker_read'}); | ||
441 | editorContext._occurrenceMarkers.push(marker); | ||
442 | } | ||
443 | for (var i = 0; i < occurrencesResult.writeRegions.length; i++) { | ||
444 | var region = occurrencesResult.writeRegions[i]; | ||
445 | var from = editor.posFromIndex(region.offset); | ||
446 | var to = editor.posFromIndex(region.offset + region.length); | ||
447 | var marker = editor.markText(from, to, {className: 'xtext-marker_write'}); | ||
448 | editorContext._occurrenceMarkers.push(marker); | ||
449 | } | ||
450 | }); | ||
451 | }, selectionUpdateDelay); | ||
452 | } | ||
453 | editor.on('cursorActivity', services.cursorActivityListener); | ||
454 | } | ||
455 | |||
456 | /** | ||
457 | * Formatting service. | ||
458 | */ | ||
459 | CodeMirrorServiceBuilder.prototype.setupFormattingService = function() { | ||
460 | var services = this.services; | ||
461 | if (services.options.enableFormattingAction) { | ||
462 | var userAgent = navigator.userAgent.toLowerCase(); | ||
463 | var formatFunction = function(editor) { | ||
464 | services.format(); | ||
465 | }; | ||
466 | services.formatKeyMap = /mac os/.test(userAgent) ? {'Shift-Cmd-F': formatFunction}: {'Shift-Ctrl-S': formatFunction}; | ||
467 | this.editor.addKeyMap(services.formatKeyMap); | ||
468 | } | ||
469 | } | ||
470 | |||
471 | return exports; | ||
472 | }); | ||